@@ -405,7 +405,7 @@
Wenn Sie registrierter Nutzer bei sourceforge.net sind, können Sie Übersetzungen
hier hochladen.
Bitte stellen Sie vorher sicher, dass keine Übersetzung für Ihre Sprache auf unserer
- Download-Seite existiert. Prüfen Sie auch hier, ob
+ Download-Seite existiert. Prüfen Sie auch hier, ob
evtl. eine Übersetzung in Arbeit oder in Vorbereitung ist.
Bitte bedenken Sie, dass wir eine Übersetzung nur auf unserer Download-Seite zur
Verfügung stellen werden, wenn Sie über ein Sourceforge.net-Benutzerkonto bereit gestellt
diff --git a/src/Greenshot/Languages/help-en-US.html b/Greenshot/Languages/help-en-US.html
similarity index 96%
rename from src/Greenshot/Languages/help-en-US.html
rename to Greenshot/Languages/help-en-US.html
index 9284bbeab..2d49ad719 100644
--- a/src/Greenshot/Languages/help-en-US.html
+++ b/Greenshot/Languages/help-en-US.html
@@ -332,7 +332,7 @@
General settings
Language: The language you prefer to be used.
- You can download additional language files for Greenshot here.
+ You can download additional language files for Greenshot here.
Launch Greenshot on startup: Start the program when the system has been booted.
Hotkeys: Customize the hotkeys to be used to create screenshots.
Use default system proxy: If checked, Greenshot uses the default system proxy to check for updates.
@@ -428,7 +428,7 @@
a lot of time and money, or if you simply like Greenshot and
the idea of open source software: please consider honoring our effort by donating.
Please have a look at our home page to see how you can support the Greenshot development team:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
@@ -447,7 +447,7 @@
If you are a registered user at sourceforge.net, you can submit translations to our
translations tracker.
Please make sure there is no existing translation for your language on our
- downloads page. Also check our translations tracker,
+ downloads page. Also check our translations tracker,
there might be a translation in progress, or at least in discussion.
Please note that we will only provide a translation on our downloads page if it has
been submitted through your sourceforge.net user account. Since we most probably are
diff --git a/src/Greenshot/Languages/help-es-ES.html b/Greenshot/Languages/help-es-ES.html
similarity index 97%
rename from src/Greenshot/Languages/help-es-ES.html
rename to Greenshot/Languages/help-es-ES.html
index fed8c6c94..e248bd90a 100644
--- a/src/Greenshot/Languages/help-es-ES.html
+++ b/Greenshot/Languages/help-es-ES.html
@@ -370,7 +370,7 @@
con una donación.
Por favor visite nuestra página web para ver como usted puede apoyar al
equipo de desarrollo de Greenshot:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
diff --git a/src/Greenshot/Languages/help-fr-FR.html b/Greenshot/Languages/help-fr-FR.html
similarity index 97%
rename from src/Greenshot/Languages/help-fr-FR.html
rename to Greenshot/Languages/help-fr-FR.html
index 751bb91ea..dc6fe3640 100644
--- a/src/Greenshot/Languages/help-fr-FR.html
+++ b/Greenshot/Languages/help-fr-FR.html
@@ -342,7 +342,7 @@
Langue: La langue que vous prfrez utiliser.
Vous pouvez tlcharger des fichiers de langues supplmentaires ici.
+ href="http://getgreenshot.org/downloads/">ici.
Lancer Greenshot au dmarrage de Windows : dmarrez le
programme lorsque le systme a t dmarr.
Raccourcis clavier: Personnaliser les raccourcis clavier
@@ -478,8 +478,8 @@
efforts en faisant un don.
Jetez un œil notre page d'accueil pour voir comment vous pouvez soutenir
l’quipe de dveloppement Greenshot :
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
Diffuser l’information
Si vous aimez Greenshot, faites le savoir autour de vous, vos amis et
@@ -494,7 +494,7 @@
soumettre votre traduction notre tracker
de traduction.
Assurez-vous qu’aucune traduction n’existe dans votre langue sur notre page de
+ target="_blank" href="http://getgreenshot.org/downloads/">page de
tlchargement. Vrifiez galement notre tracker
de traduction, pour vrifier qu’il n’y a aucune traduction en cours
ou en discussion.
diff --git a/src/Greenshot/Languages/help-hu-HU.html b/Greenshot/Languages/help-hu-HU.html
similarity index 96%
rename from src/Greenshot/Languages/help-hu-HU.html
rename to Greenshot/Languages/help-hu-HU.html
index 10792d048..5a9438813 100644
--- a/src/Greenshot/Languages/help-hu-HU.html
+++ b/Greenshot/Languages/help-hu-HU.html
@@ -235,7 +235,7 @@
ltalnos belltsok
Nyelv: A hasznlni kvnt nyelv kivlasztsa.
- Letlthet tovbbi nyelvi fjlokat a Greenshot oldalrl itt.
+ Letlthet tovbbi nyelvi fjlokat a Greenshot oldalrl itt.
Gyorsbillentyk hasznlata: Ha be van jellve, indthatod a Greenshot programot a Print + Scrn billentyvel.
Program indtsa a Windows indulsakor: a Greenshot program elindul Windows indulsakor.
Greenshot nem rhet el a kvnt nyelvet? Ha gy rzi, fordtsa le a szoftvert s kldje el neknk, szvesen fogadjuk.
Ha n regisztrl a sourceforge.net -en fel tudja tlteni a fordtst a keresnkre.
- Krjk, gyzdjn meg rla, hogy nincs e ltez fordtsa az n nyelvt a letltsi oldalunkon.
+ Krjk, gyzdjn meg rla, hogy nincs e ltez fordtsa az n nyelvt a letltsi oldalunkon.
Nzze meg a keresnkben, hogy fordts nincs e folyamatban vagy elbrls alatt.
Felhvjuk figyelmt, hogy csak akkor tudjuk a fordtst elfogadni, ha a sourceforge.net -en sajt felhasznli fikjn keresztl tlti fel.
Nagy valsznsggel nem rtjk meg az n fordtst, j esetben a tbbi soundforge felhasznl elri az n fejlesztst
diff --git a/Greenshot/Languages/help-it-IT.html b/Greenshot/Languages/help-it-IT.html
new file mode 100644
index 000000000..1d5744287
--- /dev/null
+++ b/Greenshot/Languages/help-it-IT.html
@@ -0,0 +1,395 @@
+
+
+
+
+ L'immagine può essere creata utilizzando il tasto Stamp della tastiera,
+ oppure cliccando il tasto destro del mouse sull'icona di Greenshot nella barra.
+ Ci sono varie opzioni per creare un'immagine:
+
+
+
+
Cattura regione Stamp
+
+ Il metodo cattura regione consente di selezionare una parte dello schermo da "fotografare".
+ Dopo aver avviato il metodo regione, apparirà un mirino sulla posizione del mouse sullo
+ schermo. Cliccare e tenere premuto dove si vuole impostare un angolo della regione da
+ fotografare. Tenendo premuto il pulsante del mouse, muovere il mouse fino a definire il
+ rettangolo da fotografare. Rilasciare quindi il pulsante quando il rettangolo verde avrà
+ coperto l'area da catturare nell'immagine.
+
+
+ Si può usare il tasto Spazio per cambiare da metodo regione a metodo
+ finestra.
+
+
+ Se si vuol catturare precisamente un'area, potrebbe risultare più facile selezionare
+ un'area più grande e quindi ritagliare l'immagine in
+ seguito, utilizzando la Gestione Immagini di Greenshot.
+
+
+
+
Cattura ultima regione Maiusc + Stamp
+
+ Usando questa opzione, se avete già eseguito un cattura regione o finestra,
+ si può ricatturare automaticamente la stessa regione.
+
+
+
+
Cattura finestra Alt + Stamp
+
+ Crea un'immagine della finestra che è attiva in quel momento.
+
+
+ La pagina delle impostazioni offre una possibilità per non catturare
+ direttamente la finestra attiva, consentendo quindi di sceglierne una interattivamente.
+ Se si seleziona questa opzione, la finestra può essere scelta cliccandovi (come nel metodo
+ regione, Greenshot evidenzierà l'area che verrà catturata).
+ Se si vuol catturare una finestra figlia (es: una browser
+ viewport (senza barra strumenti, ecc...) o un singolo frame di una pagina web che usa i framesets)
+ si può puntare il cursore del mouse sulla finestra e premere il tasto PgDown. Dopo di questo, sarà
+ possibile selezionare elementi da catturare nella finestra figlia.
+
+
+
+
Cattura schermo intero Ctrl + Stamp
+
+ Crea un'immagine dell'intero schermo.
+
+
Cattura Internet Explorer Ctrl + Maiusc + Stamp
+
+ Crea facilmente un'immagine della pagina web attiva in quel momento su Internet Explorer.
+ Si può usare il menu sensibile al contesto di Greenshot per selezionare la tab di Internet Explorer da catturare, oppure premere
+ Crtl + Maiusc + Stamp per catturare la tab attiva.
+
+
+
+
Uso della Gestione Immagini
+
+ Greenshot fornisce anche una pratica gestione delle immagini, che include degli utili strumenti
+ per aggiungere note e forme alle immagini. Essa permette inoltre di evidenziare o
+ offuscare parti dell'immagine.
+
+
+ La Gestioni Immagini di Greenshot non è solo per le immagini catturate. Si può usare
+ anche per aprire e modificare immagini da file o da Appunti. E' sufficiente premere il tasto destro
+ sull'icona di Greenshot nella barra, e selezionare rispettivamente Apri immagine da file
+ o Apri immagine da Appunti.
+
+
+ Come default, la gestione immagini verrà aperta ogniqualvolta un'immagine viene catturata.
+ Se non si vuole passare per la gestione immagini, si può disabilitare questo funzionamento
+ nella pagina delle impostazioni.
+
+
+
+
+
Disegnare forme
+
+ Selezionare uno degli strumenti di disegno dalla barra degli strumenti sul lato sinistro
+ della gestione immagini o dal menù Oggetti. Per facilitarne la selezione, ciascun
+ strumento è assegnato ad un tasto.
+ Le forme disponibili sono: rettangolo R, ellisse E, linea L
+ e freccia A.
+ Cliccare, tenendo premuto il pulsante del mouse e trascinare per definire la posizione e la dimensione della forma.
+ Completata la definizione, rilasciare il pulsante del mouse.
+
+
+ Le forme possono essere mosse e ridimensionate facilmente, previa selezione mediante lo strumento
+ ESC disponibile nella barra a sinistra. Per ciascun tipo di elemento c'è un gruppo di
+ opzioni specifiche per cambiarne l'aspetto (es: spessore linea,
+ colore linea, colore di riempimento). Si possono modificare le opzioni di un elemento esistente, previa selezione,
+ e anche quelle di nuovi elementi da disegnare, previa selezione dello strumento di disegno.
+
+
+ Si possono inoltre selezionare più elementi per una modifica simultanea. Per selezionare più elementi,
+ tenere premuto il tasto Maiusc mentre si clicca sugli elementi.
+
+
+
+
Aggiungere testo
+
+ L'uso dello strumento di testo T è simile all'uso degli strumenti di disegno
+ forme. E' sufficiente disegnare l'elemento di testo delle dimensioni desiderate,
+ e quindi digitare il testo.
+ Per modificare il testo di un elemento esistente, premere il doppio click sull'elemento.
+
+
+
+
Evidenziare qualcosa
+
+ Dopo aver selezionato lo strumento di evidenziazione H, definire l'area da evidenziare esattamente
+ come si volesse disegnare una forma.
+ Ci sono varie opzioni per evidenziare, esse possono essere selezionate cliccando il pulsante
+ più in alto a sinistra nella barra degli strumenti:
+
+
+
Evidenzia il testo: evidenzia un'area applicando un colore brillante ad essa, come un
+ pennarello evidenziatore
+
Evidenzia l'area: sfuoca* e scurisce tutto all'esterno dell'area selezionata
+
Scala di grigi: tutto ciò che è al di fuori dell'area selezionata viene trasformato in scala di grigi
+
Ingrandisci: l'area selezionata verrà visualizzata come ingrandita da una lente
+
+
+
+
Offuscare qualcosa
+
+ Offuscare parti di un'immagine può essere una buona idea se essa contiene dati privati che non devono essere
+ visti da altre persone, per esempio dati conto bancario, nomi, parole d'ordine o volti di persone.
+ Usare lo strumento di offuscamento O esattamente come lo strumento di evidenziazione.
+ Le opzioni disponibili per l'offuscamento, sono:
+
+
+
Offusca/pixelize: aumenta le dimensioni dei pixel nell'area selezionata
+ * A seconda delle prestazioni del proprio PC, applicare un effetto di sfumatura potrebbe rallentare la Gestione
+ Immagini di Greenshot. Se si vede che la Gestione Immagini risponde lentamente subito dopo aver eseguito una sfumatura,
+ è utile provare a ridurre il valore di Qualità anteprima nella barra strumenti di offuscamento,
+ o a diminuire il valore di Raggio sfumatura.
+ Se le prestazioni della sfumatura sono ancora deludenti per poterci lavorare, si consiglia si usare invece
+ l'effetto Offusca/pixelize.
+
+
+
+
Ritagliare l'immagine
+
+ Per ricavare solo una parte dell'immagine catturata, si può usare lo strumento di ritaglio C
+ per ritagliare l'area desiderata.
+ Dopo aver selezionato lo strumento di ritaglio, disegnare un rettangolo per l'area che si vuole mantenere.
+ Come per gli atri elementi, si possono facilmente modificare le dimensioni dell'area selezionata.
+ Dopo aver impostato correttamente la selezione dell'area, premere il pulsante di conferma della barra strumenti
+ oppure premere il tasto Invio. Si può annullare l'azione di ritaglio, cliccando il pulsante di cancellazione o premendo
+ ESC.
+
+
+ Ritaglia Automaticamente: Se si ha la necessità di ritagliare un pezzo dell'immagine lungo i bordi di uno sfondo con colore compatto,
+ è sufficiente scegliere Ritaglia Automaticamente dal menù Modifica e Greenshot automaticamente
+ selezionerà l'area di ritaglio.
+
+
+
+
Aggiunta elementi grafici all'immagine
+
+ Si possono facilmente aggiungere degli elementi grafici o delle altre immagini alla immagine in lavorazione, è sufficiente trascinare un file di immagine
+ all'interno della finestra di gestione immagini. Inoltre, selezionando Inserisci finestra dal menù Modifica, si possono inserire
+ immagini prese da altre finestre. In questo caso, appare una lista con tutte le finestre aperte, consentendo quindi di scegliere quella che si
+ vuole inserire.
+
+
+
+
+
Ri-utilizzare elementi disegnati
+
+ Se ci si ritrova a utilizzare lo stesso o simile elemento nella maggior parte delle immagini,
+ (es: campo di testo contenente tipo browser e versione, oppure offuscamento dello stesso elemento
+ su più immagini), è possibile riutilizzare gli elementi in modo semplice.
+ Selezionare Salva oggetti su file dal menù Oggetti per salvare di elementi correnti
+ per poterli riutilizzare poi. Carica oggetti da file viene invece usato per applicare su un'altra immagine
+ gli elementi salvati in precedenza.
+
+
+
+
Esportare l'immagine
+
+ Dopo aver modificato l'immagine, si può esportare il risultato per vari scopi, a seconda delle necessità.
+ Si può accedere a tutte le opzioni di esportazione mediante il menù File,
+ sulla barra principale, o per mezzo delle seguenti scorciatoie:
+
+
+
SalvaCtrl + S: salva l'immagine su un file (se l'immagine è già stata salvata, altrimenti emette la finestra di Salva come...)
+
Salva come...Ctrl + Maiusc + S: permette di scegliere la destinazione, il nome file e il formato immagine per il file da salvare
+
Copia immagine sugli appuntiCtrl + Maiusc + C: mette una copia dell'immagine sugli appunti, consentendo poi di incollarla dentro altri programmi
+
Stampa...Ctrl + P: invia l'immagine a una stampante
+
E-MailCtrl + E: apre un nuovo messaggio sul programma di e-mail di default, aggiungendo l'immagine come allegato
+
+
+ Dopo aver salvato un'immagine dalla gestione, cliccando con il tasto destro del mouse sulla barra di stato in basso sulla finestra
+ della gestione immagini, è possibile copiare il percorso sugli appunti, oppure aprire la cartella di destinazione con la gestione risorse.
+
+
+
+
+
Le Preferenze
+
+
+
Impostazioni Generali
+
+
Lingua: La lingua che si preferisce usare.
+ Si possono scaricare i file per le lingue aggiuntive di Greenshot qui.
+
Lancia Greenshot all'avvio: Avvia il programma in automatico all'accensione del sistema.
+
Scorciatoie di tastiera: Personalizza le scorciatoie (hotkeys) da usare per catturare le immagini.
+
Usa il proxy di default del sistema: Se selezionato, Greenshot usa il proxy di default del sistema per controllare se ci sono aggiornamenti.
+
Intervallo di controllo aggiornamento, in giorni: Greenshot può controllare automaticamente se ci sono aggiornamenti. Questo parametro può essere
+ utilizzato per specificare l'intervallo (in giorni); per disabilitare il controllo aggiornamento si può usare il valore 0.
+
+
+
+
Impostazioni di Cattura
+
+
Cattura puntatore mouse: Se selezionato, il puntatore del mouse verrà catturato. Il puntatore viene gestito come un elemento separato, in modo che possa essere spostato o rimosso in seguito.
+
Emetti suono fotocamera: Attiva il riscontro udibile dell'azione di cattura (suono dello scatto fotografico).
+
Millisecondi di attesa prima di catturare: Utile per aggiungere un tempo di ritardo prima dell'effettiva cattura dello schermo.
+
Usa la modalità di cattura via finestra interattiva: Invece di catturare direttamente la finestra attiva, la modalità interattiva
+ consente di selezionare la finestra da catturare. E' inoltre possibile catturare le finestre figlie, vedi Cattura finestra.
+
+ Cattura in stile Aero (so Windows Vista / 7): Se si sta usando Greenshot su Windows Vista or Windows 7 con lo stile di visualizzazione Aero abilitato, è possibile
+ scegliere come devono essere gestiti i bordi della finestra trasparente quando vengono create le immagini in modalità finestra. Questa impostazione è da usare per evitare
+ la cattura di elementi dello sfondo che appaiono attraverso i bordi trasparenti.
+
+
Automaticamente: Greenshot deciderà come gestire la trasparenza.
+
Come visualizzata: I bordi trasparenti verranno catturati come visualizzati sullo schermo.
+
Usa i colori di default: Al posto della trasparenza verrà applicato un colore compatto di default.
+
Usa colori personalizzati: Si può scegliere il colore che sarà applicato al posto della trasparenza.
+
Conserva la trasparenza: I bordi verranno catturati conservando la trasparenza, senza però catturare gli elementi che potrebbero essere sullo sfondo. (Nota: le aree
+ trasparenti verrano visualizzate usando un modello selezionato nella gestione immagini. Il modello non verrà esportato se si salverà l'immagine su file. Ricordarsi di salvare
+ il file come PNG se si vuole conservance il pieno supporto della trasparenza.)
+
+
+
Cattura Internet Explorer: Abilita la comoda cattura delle pagine web utilizzando Internet Explorer.
+
Adatta finestra Gestione a dimensioni di cattura: Se selezionata, la finestra della Gestione immagini verrà automaticamente ridimensionata in base alla dimensione dell'immagine catturata.
+
+
+
+
Impostazioni di Emissione
+
+
Destinazione dell'immagine: Consente di scegliere la destinazione/i automatiche delle immagini subito dopo l'azione di cattura.
+
Impostazioni Preferite per l'Emissione File: Cartella e nome file da usare quando si salva automaticamente, o da suggerire quando si salva (usando la finestra "Salva come"). Cliccare il pulsante ? per sapere di più sulle variabili che possono essere usate nel modello del nome file.
+
Impostazioni JPEG: Qualità da usare quando si salvano file JPEG.
+
+
+
+
Impostazioni Stampante
+
+
Riduci alle dimensioni pagina: Se l'immagine eccede le dimensioni della pagina, essa verrà ridotta e adattata alle dimensioni della pagina.
+
Ingrandisci fino alle dimensioni pagina: Se l'immagine è più piccola delle dimensioni della pagina, essa verrà ingrandita per stamparla più grande possibile senza superare le dimensioni della pagina.
+
Ruota a seconda dell'orientamento pagina: Ruoterà l'immagine in formato orizzontale di 90° per la stampa.
+
Centra nella pagina: L'immagine verrà stampata al centro della pagina.
+
Stampa data / ora sul piede della pagina: La data e l'ora di stampa verranno stampati sul piede della pagina.
+
Stampa con colori inverititi (negativo): Trasformerà l'immagine in negativo prima di stamparla, utile per esempio quando si stampa un'immagine con testo bianco su sfondo nero (per risparmiare toner o inchiostro).
+
Visualizza scelta opzioni di stampa ogni volta che si stampa un'immagine: Permette di scegliere se visualizzare o meno la finestra di scelta opzioni per le stampe successive alla prima.
+
+
+
+
Impostazioni Componenti Aggiuntivi
+
+ Visualizza la lista dei componenti aggiuntivi di Greenshot che sono attualmente installati. La configurazione del singolo componente aggiuntivo è disponibile selezionando
+ il componente dalla lista e quindi cliccando su Configura.
+
+
+
+
+
Desideri aiutarci?
+
+
+ Attualmente non abbiamo bisogno di aiuto per lo sviluppo. Tuttavia, ci sono molte cose che puoi fare per
+ supportare Greenshot e il team di sviluppo.
+ Grazie anticipatamente :)
+
+
+
+
Considera una donazione
+
+ Stiamo lavorando molto su Greenshot e stiamo spendendo molto tempo per fornire
+ un buon prodotto software gratuito e open source. Se ti sei reso conto che Greenshot
+ ti ha reso più produttivo, e se fa risparmiare a te (o alla tua società)
+ molto tempo e denaro, o se semplicemente ti piace Greenshot e l'idea
+ di software open source: per cortesia, considera di onorare i nostri sforzi con una donazione.
+ Per cortesia dai un'occhiata alla nostra home page per vedere come puoi aiutare il team di sviluppo di Greenshot:
+ http://getgreenshot.org/support/
+
+
+
+
Spargi la parola
+
+ Se ti piace Greenshot, fallo sapere anche agli altri: racconta ai tuoi amici di Greenshot.
+ Anche loro, a loro volta :)
+ Commenta positivamente Greenshot sui portali di software, oppure metti un link sulla tua home page, blog o sito web.
+
+
+
+
Invia una traduzione
+
+ Greenshot non è disponibile nella tua lingua preferita? Se ti senti in grado di tradurre un pezzo di software,
+ sei più che benvenuto.
+ Se sei un utente registrato su sourceforge.net, puoi inviare le traduzioni al nostro
+ translations tracker.
+ Prima di farlo, assicurati che non esista già la traduzione sulla nostra
+ pagina di download. Controlla anche il nostro translations tracker,
+ ci potrebbe essere una traduzione in lavorazione, o almeno in discussione.
+ Ti preghiamo di notare che forniremo una traduzione della nostra pagina di download solo se è stata inviata mediante
+ il tuo conto utente su sourceforge.net. Visto che molto probabilmente non siamo in grado di capire la traduzione, è opportuno
+ che gli altri utenti di sourceforge possano essere in grado di contattarti per revisioni o miglioramenti
+ in caso di nuove versioni di Greenshot.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Greenshot/Languages/help-ko-KR.html b/Greenshot/Languages/help-ko-KR.html
similarity index 96%
rename from src/Greenshot/Languages/help-ko-KR.html
rename to Greenshot/Languages/help-ko-KR.html
index 4b5951125..907b75a6e 100644
--- a/src/Greenshot/Languages/help-ko-KR.html
+++ b/Greenshot/Languages/help-ko-KR.html
@@ -263,7 +263,7 @@
일반 설정
사용 언어: 사용자가 사용할 언어.
- 사용할 각국 GreenShot 언어를 다운 받는 곳은 이 곳을 누르세요.
윈도우 구동시 Greenshot 실행: 윈도우를 처음 구동시 GreenShot 프로그램이 자동을 실행됩니다.
단축키: 이미지를 생성하기 위하여 사용되는 단축키를 지정합니다.
기본 시스템 프록시 사용: 체크되었을 경우 업데이트를 위한 체크시 기본 시스템 프록시를 사용합니다.
@@ -346,7 +346,7 @@
우리는 GreenShot에 많은 수고를 하고 있으며 무료이며 오픈 소스인 좋은 프로그램 제작에 많은 시간을 할애하고 있습니다.
여러분이 더 생산적으로 만들고자 한다면 또는 여러분이나 여러분 회사가 많은 시간과 돈을 절약하고자 한다면, 그리고 GreenShot과 오픈소스 소프트웨어를 좋아하신다면 기부로서 우리의 노력에 대한 도움을 부탁드립니다.
GreenShot 개발팀 지원을 하기 위한 방법은 아래 언급된 GreenShot 홈페이지를 방문해주시기 바랍니다:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
@@ -359,10 +359,10 @@
언어 번역 소개
- Greenshot에 사용할 언어가 없습니까? 사용할 언어를 넣고 싶다면 반가운 일입니다. https://sourceforge.net에 사용자등록이 되어 있다면 GreenShot 번역 상황에서 번역을 할 수 있습니다.
- GreenShot 다운로드 페이지에서 사용할 언어 번역이 없는지 먼저 확인하세요. 다음으로 GreenShot 번역 상황을 체크해보세요,
+ Greenshot에 사용할 언어가 없습니까? 사용할 언어를 넣고 싶다면 반가운 일입니다. http://sourceforge.net에 사용자등록이 되어 있다면 GreenShot 번역 상황에서 번역을 할 수 있습니다.
+ GreenShot 다운로드 페이지에서 사용할 언어 번역이 없는지 먼저 확인하세요. 다음으로 GreenShot 번역 상황을 체크해보세요,
진행 중인 번역이 있거나 또는 최소 논의 중인 것이 있을 수 있습니다.
- https://sourceforge.net 사용자 계정을 통해 등록된 언어파일만 다운로드 페이지를 통한 번역만을 제공함을 유의하시기 바랍니다.
+ http://sourceforge.net 사용자 계정을 통해 등록된 언어파일만 다운로드 페이지를 통한 번역만을 제공함을 유의하시기 바랍니다.
당신의 언어 번역본을 이해할 수 없을 때 새 Greenshot 버전일 경우에 개선하기 위하여 다른 사용자가 여러분과 연락할 수 있습니다.
diff --git a/src/Greenshot/Languages/help-nl-NL.html b/Greenshot/Languages/help-nl-NL.html
similarity index 97%
rename from src/Greenshot/Languages/help-nl-NL.html
rename to Greenshot/Languages/help-nl-NL.html
index 999bca3cc..553564ca5 100644
--- a/src/Greenshot/Languages/help-nl-NL.html
+++ b/Greenshot/Languages/help-nl-NL.html
@@ -332,7 +332,7 @@
Algemene instellingen
Taal: De taal waarin Greenshot wordt weergegeven.
- Aanvullende taalbestanden zijn te downloaden van de Greenshot website.
+ Aanvullende taalbestanden zijn te downloaden van de Greenshot website.
Greenshot met Windows opstarten: Start het programma als de PC wordt opgestart.
Sneltoetsen: Hier kunt u de sneltoetsen voor het maken van schermopnames aanpassen.
Standaard systeem-proxy gebruiken: Greenshot gebruikt de systeem-proxy instelllingen bij het zoeken naar updates.
@@ -427,7 +427,7 @@
tijd en geld bespaart, of u vindt het gewoon een fijn programma en
u staat positief tegenover het concept van open source software: Ondersteun ons werk dan met een donatie.
Bezoek onze website en lees daar hoe u het Greenshot-team kunt ondersteunen:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
diff --git a/src/Greenshot/Languages/help-nn-NO.html b/Greenshot/Languages/help-nn-NO.html
similarity index 96%
rename from src/Greenshot/Languages/help-nn-NO.html
rename to Greenshot/Languages/help-nn-NO.html
index 2e29a9064..f4ff1d77a 100644
--- a/src/Greenshot/Languages/help-nn-NO.html
+++ b/Greenshot/Languages/help-nn-NO.html
@@ -301,7 +301,7 @@
Generelle innstillingar
Sprk: Sprket du nskjer bruke i Greenshot.
- Du kan laste ned tilleggssprk her.
Start Greenshot med systemet: Aktiverar Greenshot nr Windows startar
Snggtastar: Redigerar snggtastane knytt til skjermknipsing
Bruk systemets standardproxy: Om dette er huka av, vil Greenshot bruke systemet sin proxyserver for sj etter oppdateringar.
@@ -412,7 +412,7 @@
du rett og slett likar ideen om open kjeldekode er du velkomen til re innsatsen
vr med ein donasjon.
P heimesida vr finn du informasjon om korleis du kan sttte utviklarane:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
@@ -421,7 +421,7 @@
Om du likar Greenshot, syt for at andre fr greie p det! Fortel vener, kollegaer og
fygljarar p sosiale media om Greenshot!
Gje tilbakemelding p Greenshot p relevante programvaresider eller legg ei
- lenkje til nettsida vr (https://getgreenshot.org) p bloggen eller heimesida di.
+ lenkje til nettsida vr (http://getgreenshot.org) p bloggen eller heimesida di.
@@ -432,7 +432,7 @@
Om du er registrert p sourceforge.net kan du sende inn omsetjingar til vr
omsetjings-tracker.
Kontroller fyrst om det alt finst ei omsetjing p
- nedlastingssida.
+ nedlastingssida.
Sjekk g omsetjings-trackeren,
d den kan innehalde ein omsetjing under arbeid eller diskusjon.
Ver merksam p at me bare tilbyr omsetjingar p nedlastingssida vr om den har
diff --git a/src/Greenshot/Languages/help-pl-PL.html b/Greenshot/Languages/help-pl-PL.html
similarity index 97%
rename from src/Greenshot/Languages/help-pl-PL.html
rename to Greenshot/Languages/help-pl-PL.html
index 555d2038e..7e2ad2396 100644
--- a/src/Greenshot/Languages/help-pl-PL.html
+++ b/Greenshot/Languages/help-pl-PL.html
@@ -303,7 +303,7 @@
dużo czasu i pieniędzy, czy po prostu podobnie jak Greenshot, popierasz ideę
oprogramowania open source:. rozważ uhonorowanie naszych wysiłków, przekazując darowiznę
Proszę spojrzeć na naszą stronę domową, aby zobaczyć, jak można wspierać zespół rozwoju Greenshot
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
diff --git a/src/Greenshot/Languages/help-pt-BR.html b/Greenshot/Languages/help-pt-BR.html
similarity index 98%
rename from src/Greenshot/Languages/help-pt-BR.html
rename to Greenshot/Languages/help-pt-BR.html
index ef817b2bb..4c1c913a1 100644
--- a/src/Greenshot/Languages/help-pt-BR.html
+++ b/Greenshot/Languages/help-pt-BR.html
@@ -333,7 +333,7 @@
Configuraes Gerais
Idioma: O idioma preferido a usar.
- Pode transferir arquivos de idiomas adicionais para o Greenshot aqui.
+ Pode transferir arquivos de idiomas adicionais para o Greenshot aqui.
Iniciar o Greenshot no inicializao: Inicia o programa no inicializao do sistema.
Teclas de Atalho: Personaliza as teclas de atalho a serem usadas para criar capturas.
Usar proxy padro do sistema: Se selecionado, o Greenshot usa proxy padro do sistema para procurar atualizaes.
@@ -428,7 +428,7 @@
muito tempo e dinheiro, ou se simplesmente gosta do Greenshot e
da ideia de software de cdigo aberto: por favor considere compensar o nosso esforo, doando.
Por favor visite a nossa pgina web para ver como pode apoiar a equipe de desenvolvimento do Greenshot:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
@@ -447,7 +447,7 @@
Se um usurio registado no sourceforge.net, pode enviar tradues para o nosso
monitor de tradues.
Por favor certifique-se de que no existe traduo para o seu idioma na nossa
- pgina de downloads. Veja tambm no nosso monitor de tradues,
+ pgina de downloads. Veja tambm no nosso monitor de tradues,
se existe alguma traduo a decorrer, ou pelo menos em discusso.
Tenha em ateno que s disponibilizaremos uma traduo na nossa pgina de downloads se ela
tiver sido enviada atravs da sua conta no sourceforge.net. Uma vez que o mais provvel ns
diff --git a/src/Greenshot/Languages/help-pt-PT.html b/Greenshot/Languages/help-pt-PT.html
similarity index 98%
rename from src/Greenshot/Languages/help-pt-PT.html
rename to Greenshot/Languages/help-pt-PT.html
index 9709eb0b0..40467a293 100644
--- a/src/Greenshot/Languages/help-pt-PT.html
+++ b/Greenshot/Languages/help-pt-PT.html
@@ -333,7 +333,7 @@
Definies Gerais
Idioma: O idioma preferido a utilizar.
- Pode transferir ficheiros de idiomas adicionais para o Greenshot aqui.
+ Pode transferir ficheiros de idiomas adicionais para o Greenshot aqui.
Iniciar o Greenshot no arranque: Inicia o programa no arranque do sistema.
Teclas de Atalho: Personaliza as teclas de atalho a serem usadas para criar capturas.
Usar proxy padro do sistema: Se seleccionado, o Greenshot usa proxy padro do sistema para procurar actualizaes.
@@ -429,7 +429,7 @@
muito tempo e dinheiro, ou se simplesmente gosta do Greenshot e
da ideia de sotware de cdigo aberto: por favor considere compensar o nosso esforo, doando.
Por favor visite a nossa pgina web para ver como pode apoiar a equipa de desenvolvimento do Greenshot:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
@@ -448,7 +448,7 @@
Se um utilizador registado no sourceforge.net, pode enviar tradues para o nosso
monitor de tradues.
Por favor certifique-se de que no existe traduo para o seu idioma na nossa
- pgina de transferncias. Veja tambm no nosso monitor de tradues,
+ pgina de transferncias. Veja tambm no nosso monitor de tradues,
se existe alguma traduo a decorrer, ou pelo menos em discusso.
Tenha em ateno que s disponibilizaremos uma traduo na nossa pgina de transferncias se ela
tiver sido enviada atravs da sua conta no sourceforge.net. Uma vez que o mais provvel ns
diff --git a/src/Greenshot/Languages/help-ru-RU.html b/Greenshot/Languages/help-ru-RU.html
similarity index 97%
rename from src/Greenshot/Languages/help-ru-RU.html
rename to Greenshot/Languages/help-ru-RU.html
index a9b022dca..33eb7d114 100644
--- a/src/Greenshot/Languages/help-ru-RU.html
+++ b/Greenshot/Languages/help-ru-RU.html
@@ -283,7 +283,7 @@
Общие
Язык (Language): Язык интерфейса.
- Вы можете скачать дополнительные языковые файлы здесь.
+ Вы можете скачать дополнительные языковые файлы здесь.
Автозагрузка: Запуск программы при загрузке системы.
Горячие клавиши: Настройка горячих клавиш, которые будут использоваться для создания скриншотов.
Использовать прокси-сервер системы по умолчанию: Если флажок установлен, Greenshot использует прокси-сервер по умолчанию системы для проверки обновлений.
@@ -359,7 +359,7 @@
сберегает вам или вашей компании время и деньги, или если Greenshot вам просто нравится, вы можете пожертвовать
некоторую сумму денег, чтобы поддержать дальнейшую разработку,
перейдите на нашу домашнюю страницу, чтобы узнать, как вы можете поддержать команду разработчиков Greenshot:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
@@ -380,7 +380,7 @@
If you are a registered user at sourceforge.net, you can submit translations to our
translations tracker.
Please make sure there is no existing translation for your language on our
- downloads page. Also check our translations tracker,
+ downloads page. Also check our translations tracker,
there might be a translation in progress, or at least in discussion.
Please note that we will only provide a translation on our downloads page if it has
been submitted through your sourceforge.net user account. Since we most probably are
diff --git a/src/Greenshot/Languages/help-sv-SE.html b/Greenshot/Languages/help-sv-SE.html
similarity index 96%
rename from src/Greenshot/Languages/help-sv-SE.html
rename to Greenshot/Languages/help-sv-SE.html
index 8ca8737e4..cdff974f1 100644
--- a/src/Greenshot/Languages/help-sv-SE.html
+++ b/Greenshot/Languages/help-sv-SE.html
@@ -263,7 +263,7 @@
Sprk: Det sprk du fredrar.
- Du kan ladda ner ytterligare sprkfiler fr Greenshot hr.
+ Du kan ladda ner ytterligare sprkfiler fr Greenshot hr.
Starta Greenshot nr datorn startas: Starta programmet nr systemet startat upp.
@@ -358,7 +358,7 @@
Vi lgger ner mycket arbete p Greenshot och spenderar en hel del tid till att tillhandahlla ett bra program helt gratis och med ppen kllkod. Om du tycker att programmet gr dig mer produktiv, om det sparar dig (eller ditt fretag) en massa tid och pengar, eller om du helt enkelt bara tycker om Greenshot och tanken p mjukvara med ppen kllkod: vervg att belna vrt arbete genom att donera.
Ta en titt p vr hemsida fr f reda p hur du kan stdja Greenshots utvecklingsteam:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
@@ -374,7 +374,7 @@
r inte Greenshot tillgngligt p ditt sprk? Om du knner dig trygg med att verstta mjukvara r du mer n vlkommen att gra s.
Om du r en registrerad anvndare p sourceforge.net kan du skicka in versttningar till vr versttnings-tracker.
Se till s att det inte finns ngon existerande versttning till ditt sprk p vr
- nerladdningssida. Kolla ocks vr versttnings-tracker,
+ nerladdningssida. Kolla ocks vr versttnings-tracker,
det kan finnas pgende versttningar, eller i alla fall i planeringsstadiet.
Observera att vi enbart tillhandahller en versttning p vr nerladdningssida om den blivit inskickad via ditt konto p sourceforge.net. Eftersom vi sannolikt inte har mjlighet att frst din versttning r det bra om andra anvndare p sourceforge kan kontakta dig angende frbttringar eller ndringar om en ny version av Greenshot slpps.
diff --git a/src/Greenshot/Languages/help-tr-TR.html b/Greenshot/Languages/help-tr-TR.html
similarity index 95%
rename from src/Greenshot/Languages/help-tr-TR.html
rename to Greenshot/Languages/help-tr-TR.html
index 11161cf9f..81705a566 100644
--- a/src/Greenshot/Languages/help-tr-TR.html
+++ b/Greenshot/Languages/help-tr-TR.html
@@ -260,7 +260,7 @@
Genel ayarlar
Dil: Programı kullanmayı tercih ettiğiniz dil.
- Greenshot için kullanılabilecek diğer dilleri buradan indirebilirsiniz.
Kısayol tuşlarını devral: İşaretlediğinizde, Greenshot Print tuşu ile kullanılabilir.
Windows başlangıcında çalıştır: Bilgisayar başlatıldığında programı çalıştır.
Flaş etkisi: Ekran yakalarken görsel flaş çakması etkisi oluşturur
@@ -303,7 +303,7 @@
veya kurumunuza para ve zaman kazandırıyorsa ya da Greenshot ve açık kaynak felsefesinden
hoşlanıyorsanız lütfen bağış yaparak emeklerimize saygı gösterin.
Greenshot geliştirici takımını nasıl destekleyeceğinizi görmek için web sitemize bakın:
- https://getgreenshot.org/support/
+ http://getgreenshot.org/support/
@@ -321,7 +321,7 @@
Eğer kayıtlı bir sourceforge.net üyesi iseniz, çevirilerinizi
çeviri izleyicimize gönderbilirsiniz.
Öncesinde
- indirme sayfasından dilinizde bir çeviri olmadığından emin olun. Ayrıca çeviri izleyicimizden de durumu denetleyin.
+ indirme sayfasından dilinizde bir çeviri olmadığından emin olun. Ayrıca çeviri izleyicimizden de durumu denetleyin.
Dilinizde bir çeviri çalışması planlanıyor veya yapılıyor olabilir.
Çevirileri indirme sayfalarımızda ancak sourceforge.net kullanıcı hesabınızla gönderdiyseniz
yayınlayacağız. Böylece bizler çevirinizi büyük olasılıkla anlayamayacağımız için diğer sourceforge.
diff --git a/src/Greenshot/Languages/help-zh-CN.html b/Greenshot/Languages/help-zh-CN.html
similarity index 95%
rename from src/Greenshot/Languages/help-zh-CN.html
rename to Greenshot/Languages/help-zh-CN.html
index 74c9b1387..c3e2e8402 100644
--- a/src/Greenshot/Languages/help-zh-CN.html
+++ b/Greenshot/Languages/help-zh-CN.html
@@ -272,7 +272,7 @@
+ Aktivieren Sie den Bereichsmodus, indem Sie die Druck-Taste auf
+ Ihrer Tastatur betätigen oder Bereich abfotografieren aus dem
+ Kontextmenü wählen.
+ Drücken und halten Sie die linke Maustaste gedrückt, um einen rechteckigen
+ Bereich zu definieren, der abfotografiert werden soll.
+ Nach dem Loslassen der Maustaste öffnet sich das Bildbearbeitungsfenster
+ zur weiteren Bearbeitung Ihres Screenshots.
+ Um den Bereich später noch einmal abzufotografieren, wählen Sie Zuletzt
+ gewählten Bereich abfotografieren.
+
+
+
Fenstermodus
+
+
+ Aktivieren Sie den FensterModus, indem Sie Alt + Druck auf
+ Ihrer Tastatur betätigen oder Fenster abfotografieren aus dem
+ Kontextmenü wählen.
+ Klicken Sie auf das Fenster, dass abfotografiern werden soll.
+ Nachdem Sie geklickt haben öffnet sich das Bildbearbeitungsfenster
+ zur weiteren Bearbeitung Ihres Screenshots.
+
+
+
Bildschirmmodus
+
+
+ Wenn Sie den gesamten Bildschirm abfotografieren wollen, drücken Sie einfach
+ Ctrl + Print auf Ihrer Tastatur oder wählen Sie Kompletten
+ Bildschirm abfotografieren.
+ Der komplette Bildschirm wird sofort abfotografiert, das Bildbearbeitungsfenster
+ öffnet sich zur weiteren Bearbeitung Ihres Screenshots.
+
+ Wenn Sie das Bildbearbeitungsfenster nicht nutzen wollen knnen Sie im
+ Kontextmen oder im Einstellungsdialog festlegen, dass es nicht angezeigt
+ werden soll. In diesem Fall wird der Screenshot sofort in eine Datei
+ gespeichert. Speicherort, Dateiname und Bildformat sind dann abhngig von
+ den bevorzugten Ausgabedatei-Einstellungen im Einstellungsdialog.
+
+
+
Datei-Menü
+
+
+
Speichern: speichert die Grafik unter dem Pfad der beim letzten Speichern unter... Dialog gewählt wurde
+
Speichern unter...: öffnet einen Dialog zur Auswahl des Pfads und Dateinamens unter dem die Grafik gespeichert werden soll
+
Grafik in die Zwischenablage kopieren: kopiert die Grafik in die Zwischenablage, so dass sie in anderer Software verwendet werden kann
+
+
+
Bearbeiten-Menü
+
+
+
Gewähltes Element in die Zwischenablage ausschneiden: entfernt das ausgewähltes Element und kopiert es in die Zwischenablage, so dass es in ein anderes Bildbearbeitungsfenster eingefügt werden kann
+
Gewähltes Element in die Zwischenablage kopieren: kopiert das ausgewähltes Element in die Zwischenablage, so dass es in ein anderes Bildbearbeitungsfenster eingefügt werden kann
+
Element aus der Zwischenablage einfügen: fügt ein vorher ausgeschnittenes/kopiertes Element in das Bildbearbeitungsfenster ein
+
Gewähltes Element duplizieren: dupliziert das gewählte Element
+
+
+
Objekt-Menü
+
+
+
Rechteck hinzufügen: fügt ein Rechteck zur Grafik hinzu
+
Ellipse hinzufügen: fügt eine Ellipse zur Grafik hinzu
+
Textfeld hinzufügen: fügt ein Textfeld zur Grafik hinzu
+
Gewähltes Element löschen: entfernt das gewählte Element aus der Grafik
+
+
+
+ Klicken Sie ein Element an um es auszuwählen. Anschließend können Sie die Größe oder
+ Position verändern, oder es kopieren, ausschneiden oder entfernen. Die Größe
+ eines Elements kann durch Klicken und Ziehen der Anfasser (kleine schwarze
+ Quadrate) an der linken oberen oder der rechten unteren Ecke geändert werden.
+
+ Activate region mode by hitting the Print key on your keyboard
+ or by choosing Capture region from the context menu.
+ Left-click and drag to define a rectangular area you want to be shot.
+ After releasing the mouse button, the image editor window will open for
+ further editing of your screenshot.
+ For shooting the region again later, choose Capture last region from
+ the context menu.
+
+
+
Window mode
+
+
+ Activate window mode by hitting Alt + Print on your keyboard
+ or by choosing Capture window from the context menu.
+ Click the window you want to be shot.
+ After clicking, the image editor window will open for
+ further editing of your screenshot.
+
+
+
Fullscreen mode
+
+
+ If you want to shoot the complete screen, just press Ctrl + Print on
+ your keyboard or choose Capture full screen from the context menu.
+ The complete screen will be shot instantly, the image editor window will open for
+ further editing of your screenshot.
+
+ If you do not want to use the image editor window you can choose to skip
+ in in the context menu or in the settings dialog. The screenshot will be
+ saved directly to a file then. Storage location, filename and image format
+ are defined by your preferred output file settings in the settings dialog.
+
+
+
File menu
+
+
+
Save: saves the image to the file specified in the last Save as... dialog
+
Save as...: lets you choose a path and filename to save the image to
+
Copy image to clipboard: copies the image to the clipboard, for pasting it to other software
+
+
+
Edit menu
+
+
+
Cut selected element to clipboard: removes the selected element and copies it to the clipboard, so that it can be pasted into another image editor window
+
Copy selected element to clipboard: copies it to the clipboard, so that it can be pasted into another image editor window
+
Paste element from clipboard: pastes a previously cut/copied element into the image editor window
+
Duplicate selected element: duplicates the selected element
+
+
+
Object menu
+
+
+
Add rectangle: adds a rectangle to the image
+
Add ellipse: adds an ellipse to the image
+
Add textbox: adds a textbox to the image
+
Delete selected element: removes the selected element from the image
+
+
+
+ Click an element to select it for resizing, moving, copying, cutting, or removal. The size of an
+ element can be defined by dragging the grippers (small black squares) at the top-left and the
+ bottom-right corner of the selected element.
+
+ Revolutionary screenshot tool optimized for productivity. Save a screenshot or a part of the screen to a file within a second. Apply text and shapes to the screenshot. Offers capture of window, region or full screenshot. Supports several image formats.
+
();
+ form.BeginInvoke((MethodInvoker)delegate
+ {
+ var historyMenuItem = _historyMenuItem;
+ if (historyMenuItem == null)
+ {
+ return;
+ }
+ if (_config?.ImgurUploadHistory != null && _config.ImgurUploadHistory.Count > 0) {
+ historyMenuItem.Enabled = true;
+ } else {
+ historyMenuItem.Enabled = false;
+ }
+ });
+ } catch (Exception ex) {
+ Log.Error("Error loading history", ex);
+ }
+ }
+
+ public virtual void Shutdown() {
+ Log.Debug("Imgur Plugin shutdown.");
+ Language.LanguageChanged -= OnLanguageChanged;
+ }
+
+ ///
+ /// Implementation of the IPlugin.Configure
+ ///
+ public virtual void Configure() {
+ _config.ShowConfigDialog();
+ }
+
+ ///
+ /// Upload the capture to imgur
+ ///
+ /// ICaptureDetails
+ /// ISurface
+ /// out string for the url
+ /// true if the upload succeeded
+ public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) {
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors);
+ try {
+ string filename = Path.GetFileName(FilenameHelper.GetFilenameFromPattern(_config.FilenamePattern, _config.UploadFormat, captureDetails));
+ ImgurInfo imgurInfo = null;
+
+ // Run upload in the background
+ new PleaseWaitForm().ShowAndWait("Imgur plug-in", Language.GetString("imgur", LangKey.communication_wait),
+ delegate
+ {
+ imgurInfo = ImgurUtils.UploadToImgur(surfaceToUpload, outputSettings, captureDetails.Title, filename);
+ if (imgurInfo != null && _config.AnonymousAccess) {
+ Log.InfoFormat("Storing imgur upload for hash {0} and delete hash {1}", imgurInfo.Hash, imgurInfo.DeleteHash);
+ _config.ImgurUploadHistory.Add(imgurInfo.Hash, imgurInfo.DeleteHash);
+ _config.runtimeImgurHistory.Add(imgurInfo.Hash, imgurInfo);
+ UpdateHistoryMenuItem();
+ }
+ }
+ );
+
+ if (imgurInfo != null) {
+ // TODO: Optimize a second call for export
+ using (Image tmpImage = surfaceToUpload.GetImageForExport()) {
+ imgurInfo.Image = ImageHelper.CreateThumbnail(tmpImage, 90, 90);
+ }
+ IniConfig.Save();
+
+ if (_config.UsePageLink)
+ {
+ uploadUrl = imgurInfo.Page;
+ }
+ else
+ {
+ uploadUrl = imgurInfo.Original;
+ }
+ if (!string.IsNullOrEmpty(uploadUrl) && _config.CopyLinkToClipboard)
+ {
+ try
+ {
+ ClipboardHelper.SetClipboardData(uploadUrl);
+
+ }
+ catch (Exception ex)
+ {
+ Log.Error("Can't write to clipboard: ", ex);
+ uploadUrl = null;
+ }
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ Log.Error("Error uploading.", e);
+ MessageBox.Show(Language.GetString("imgur", LangKey.upload_failure) + " " + e.Message);
+ }
+ uploadUrl = null;
+ return false;
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Imgur/ImgurPlugin.resx b/GreenshotImgurPlugin/ImgurPlugin.resx
similarity index 100%
rename from src/Greenshot.Plugin.Imgur/ImgurPlugin.resx
rename to GreenshotImgurPlugin/ImgurPlugin.resx
diff --git a/GreenshotImgurPlugin/ImgurUtils.cs b/GreenshotImgurPlugin/ImgurUtils.cs
new file mode 100644
index 000000000..817ff44fa
--- /dev/null
+++ b/GreenshotImgurPlugin/ImgurUtils.cs
@@ -0,0 +1,350 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Net;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotImgurPlugin {
+ ///
+ /// A collection of Imgur helper methods
+ ///
+ public static class ImgurUtils {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurUtils));
+ private const string SmallUrlPattern = "http://i.imgur.com/{0}s.jpg";
+ private static readonly ImgurConfiguration Config = IniConfig.GetIniSection();
+ private const string AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}";
+ private const string TokenUrl = "https://api.imgur.com/oauth2/token";
+
+ ///
+ /// Check if we need to load the history
+ ///
+ ///
+ public static bool IsHistoryLoadingNeeded()
+ {
+ Log.InfoFormat("Checking if imgur cache loading needed, configuration has {0} imgur hashes, loaded are {1} hashes.", Config.ImgurUploadHistory.Count, Config.runtimeImgurHistory.Count);
+ return Config.runtimeImgurHistory.Count != Config.ImgurUploadHistory.Count;
+ }
+
+ ///
+ /// Load the complete history of the imgur uploads, with the corresponding information
+ ///
+ public static void LoadHistory() {
+ if (!IsHistoryLoadingNeeded())
+ {
+ return;
+ }
+
+ bool saveNeeded = false;
+
+ // Load the ImUr history
+ foreach (string hash in Config.ImgurUploadHistory.Keys.ToList()) {
+ if (Config.runtimeImgurHistory.ContainsKey(hash)) {
+ // Already loaded
+ continue;
+ }
+
+ try
+ {
+ var deleteHash = Config.ImgurUploadHistory[hash];
+ ImgurInfo imgurInfo = RetrieveImgurInfo(hash, deleteHash);
+ if (imgurInfo != null) {
+ RetrieveImgurThumbnail(imgurInfo);
+ Config.runtimeImgurHistory[hash] = imgurInfo;
+ } else {
+ Log.InfoFormat("Deleting unknown ImgUr {0} from config, delete hash was {1}.", hash, deleteHash);
+ Config.ImgurUploadHistory.Remove(hash);
+ Config.runtimeImgurHistory.Remove(hash);
+ saveNeeded = true;
+ }
+ } catch (WebException wE) {
+ bool redirected = false;
+ if (wE.Status == WebExceptionStatus.ProtocolError) {
+ HttpWebResponse response = (HttpWebResponse)wE.Response;
+
+ if (response.StatusCode == HttpStatusCode.Forbidden)
+ {
+ Log.Error("Imgur loading forbidden", wE);
+ break;
+ }
+ // Image no longer available?
+ if (response.StatusCode == HttpStatusCode.Redirect) {
+ Log.InfoFormat("ImgUr image for hash {0} is no longer available, removing it from the history", hash);
+ Config.ImgurUploadHistory.Remove(hash);
+ Config.runtimeImgurHistory.Remove(hash);
+ redirected = true;
+ }
+ }
+ if (!redirected) {
+ Log.Error("Problem loading ImgUr history for hash " + hash, wE);
+ }
+ } catch (Exception e) {
+ Log.Error("Problem loading ImgUr history for hash " + hash, e);
+ }
+ }
+ if (saveNeeded) {
+ // Save needed changes
+ IniConfig.Save();
+ }
+ }
+
+ ///
+ /// Use this to make sure Imgur knows from where the upload comes.
+ ///
+ ///
+ private static void SetClientId(HttpWebRequest webRequest) {
+ webRequest.Headers.Add("Authorization", "Client-ID " + ImgurCredentials.CONSUMER_KEY);
+ }
+
+ ///
+ /// Do the actual upload to Imgur
+ /// For more details on the available parameters, see: http://api.imgur.com/resources_anon
+ ///
+ /// ISurface to upload
+ /// OutputSettings for the image file format
+ /// Title
+ /// Filename
+ /// ImgurInfo with details
+ public static ImgurInfo UploadToImgur(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) {
+ IDictionary otherParameters = new Dictionary();
+ // add title
+ if (title != null && Config.AddTitle) {
+ otherParameters["title"]= title;
+ }
+ // add filename
+ if (filename != null && Config.AddFilename) {
+ otherParameters["name"] = filename;
+ }
+ string responseString = null;
+ if (Config.AnonymousAccess) {
+ // add key, we only use the other parameters for the AnonymousAccess
+ //otherParameters.Add("key", IMGUR_ANONYMOUS_API_KEY);
+ HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(Config.ImgurApi3Url + "/upload.xml?" + NetworkHelper.GenerateQueryParameters(otherParameters), HTTPMethod.POST);
+ webRequest.ContentType = "image/" + outputSettings.Format;
+ webRequest.ServicePoint.Expect100Continue = false;
+
+ SetClientId(webRequest);
+ try {
+ using (var requestStream = webRequest.GetRequestStream()) {
+ ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings);
+ }
+
+ using WebResponse response = webRequest.GetResponse();
+ LogRateLimitInfo(response);
+ var responseStream = response.GetResponseStream();
+ if (responseStream != null)
+ {
+ using StreamReader reader = new StreamReader(responseStream, true);
+ responseString = reader.ReadToEnd();
+ }
+ } catch (Exception ex) {
+ Log.Error("Upload to imgur gave an exeption: ", ex);
+ throw;
+ }
+ } else {
+
+ var oauth2Settings = new OAuth2Settings
+ {
+ AuthUrlPattern = AuthUrlPattern,
+ TokenUrl = TokenUrl,
+ RedirectUrl = "https://imgur.com",
+ CloudServiceName = "Imgur",
+ ClientId = ImgurCredentials.CONSUMER_KEY,
+ ClientSecret = ImgurCredentials.CONSUMER_SECRET,
+ AuthorizeMode = OAuth2AuthorizeMode.EmbeddedBrowser,
+ BrowserSize = new Size(680, 880),
+ RefreshToken = Config.RefreshToken,
+ AccessToken = Config.AccessToken,
+ AccessTokenExpires = Config.AccessTokenExpires
+ };
+
+ // Copy the settings from the config, which is kept in memory and on the disk
+
+ try
+ {
+ var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, Config.ImgurApi3Url + "/upload.xml", oauth2Settings);
+ otherParameters["image"] = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
+
+ NetworkHelper.WriteMultipartFormData(webRequest, otherParameters);
+
+ responseString = NetworkHelper.GetResponseAsString(webRequest);
+ }
+ finally
+ {
+ // Copy the settings back to the config, so they are stored.
+ Config.RefreshToken = oauth2Settings.RefreshToken;
+ Config.AccessToken = oauth2Settings.AccessToken;
+ Config.AccessTokenExpires = oauth2Settings.AccessTokenExpires;
+ Config.IsDirty = true;
+ IniConfig.Save();
+ }
+ }
+ if (string.IsNullOrEmpty(responseString))
+ {
+ return null;
+ }
+ return ImgurInfo.ParseResponse(responseString);
+ }
+
+ ///
+ /// Retrieve the thumbnail of an imgur image
+ ///
+ ///
+ public static void RetrieveImgurThumbnail(ImgurInfo imgurInfo) {
+ if (imgurInfo.SmallSquare == null) {
+ Log.Warn("Imgur URL was null, not retrieving thumbnail.");
+ return;
+ }
+ Log.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare);
+ HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(string.Format(SmallUrlPattern, imgurInfo.Hash), HTTPMethod.GET);
+ webRequest.ServicePoint.Expect100Continue = false;
+ // Not for getting the thumbnail, in anonymous modus
+ //SetClientId(webRequest);
+ using WebResponse response = webRequest.GetResponse();
+ LogRateLimitInfo(response);
+ Stream responseStream = response.GetResponseStream();
+ if (responseStream != null)
+ {
+ imgurInfo.Image = ImageHelper.FromStream(responseStream);
+ }
+ }
+
+ ///
+ /// Retrieve information on an imgur image
+ ///
+ ///
+ ///
+ /// ImgurInfo
+ public static ImgurInfo RetrieveImgurInfo(string hash, string deleteHash) {
+ string url = Config.ImgurApi3Url + "/image/" + hash + ".xml";
+ Log.InfoFormat("Retrieving Imgur info for {0} with url {1}", hash, url);
+ HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.GET);
+ webRequest.ServicePoint.Expect100Continue = false;
+ SetClientId(webRequest);
+ string responseString = null;
+ try
+ {
+ using WebResponse response = webRequest.GetResponse();
+ LogRateLimitInfo(response);
+ var responseStream = response.GetResponseStream();
+ if (responseStream != null)
+ {
+ using StreamReader reader = new StreamReader(responseStream, true);
+ responseString = reader.ReadToEnd();
+ }
+ } catch (WebException wE) {
+ if (wE.Status == WebExceptionStatus.ProtocolError) {
+ if (((HttpWebResponse)wE.Response).StatusCode == HttpStatusCode.NotFound) {
+ return null;
+ }
+ }
+ throw;
+ }
+ ImgurInfo imgurInfo = null;
+ if (responseString != null)
+ {
+ Log.Debug(responseString);
+ imgurInfo = ImgurInfo.ParseResponse(responseString);
+ imgurInfo.DeleteHash = deleteHash;
+ }
+ return imgurInfo;
+ }
+
+ ///
+ /// Delete an imgur image, this is done by specifying the delete hash
+ ///
+ ///
+ public static void DeleteImgurImage(ImgurInfo imgurInfo) {
+ Log.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash);
+
+ try {
+ string url = Config.ImgurApi3Url + "/image/" + imgurInfo.DeleteHash + ".xml";
+ HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.DELETE);
+ webRequest.ServicePoint.Expect100Continue = false;
+ SetClientId(webRequest);
+ string responseString = null;
+ using (WebResponse response = webRequest.GetResponse()) {
+ LogRateLimitInfo(response);
+ var responseStream = response.GetResponseStream();
+ if (responseStream != null)
+ {
+ using StreamReader reader = new StreamReader(responseStream, true);
+ responseString = reader.ReadToEnd();
+ }
+ }
+ Log.InfoFormat("Delete result: {0}", responseString);
+ } catch (WebException wE) {
+ // Allow "Bad request" this means we already deleted it
+ if (wE.Status == WebExceptionStatus.ProtocolError) {
+ if (((HttpWebResponse)wE.Response).StatusCode != HttpStatusCode.BadRequest) {
+ throw ;
+ }
+ }
+ }
+ // Make sure we remove it from the history, if no error occured
+ Config.runtimeImgurHistory.Remove(imgurInfo.Hash);
+ Config.ImgurUploadHistory.Remove(imgurInfo.Hash);
+ imgurInfo.Image = null;
+ }
+
+ ///
+ /// Helper for logging
+ ///
+ ///
+ ///
+ private static void LogHeader(IDictionary nameValues, string key) {
+ if (nameValues.ContainsKey(key)) {
+ Log.InfoFormat("{0}={1}", key, nameValues[key]);
+ }
+ }
+
+ ///
+ /// Log the current rate-limit information
+ ///
+ ///
+ private static void LogRateLimitInfo(WebResponse response) {
+ IDictionary nameValues = new Dictionary();
+ foreach (string key in response.Headers.AllKeys) {
+ if (!nameValues.ContainsKey(key)) {
+ nameValues.Add(key, response.Headers[key]);
+ }
+ }
+ LogHeader(nameValues, "X-RateLimit-Limit");
+ LogHeader(nameValues, "X-RateLimit-Remaining");
+ LogHeader(nameValues, "X-RateLimit-UserLimit");
+ LogHeader(nameValues, "X-RateLimit-UserRemaining");
+ LogHeader(nameValues, "X-RateLimit-UserReset");
+ LogHeader(nameValues, "X-RateLimit-ClientLimit");
+ LogHeader(nameValues, "X-RateLimit-ClientRemaining");
+
+ // Update the credits in the config, this is shown in a form
+ if (nameValues.ContainsKey("X-RateLimit-Remaining") && int.TryParse(nameValues["X-RateLimit-Remaining"], out var credits)) {
+ Config.Credits = credits;
+ }
+ }
+ }
+}
diff --git a/GreenshotImgurPlugin/LanguageKeys.cs b/GreenshotImgurPlugin/LanguageKeys.cs
new file mode 100644
index 000000000..c0aeb0295
--- /dev/null
+++ b/GreenshotImgurPlugin/LanguageKeys.cs
@@ -0,0 +1,41 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotImgurPlugin {
+ public enum LangKey {
+ upload_menu_item,
+ settings_title,
+ label_url,
+ label_upload_format,
+ label_clear,
+ OK,
+ CANCEL,
+ upload_success,
+ upload_failure,
+ communication_wait,
+ delete_question,
+ clear_question,
+ delete_title,
+ use_page_link,
+ history,
+ configure
+ }
+}
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-cs-CZ.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-cs-CZ.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-cs-CZ.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-cs-CZ.xml
index 07020ce21..2b7a62e83 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-cs-CZ.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-cs-CZ.xml
@@ -1,21 +1,21 @@
-
-
-
- Použít anonymní přístup
- Zrušit
- Jste si jisti, že chcete smazat místní historii Imgur?
- Komunikace s Imgur. Prosím čekejte ...
- Nastavení
- Jste si jisti, že chcete smazat obrázek {0} z Imgur?
- Odstranit Imgur {0}
- Historie
- Formát obrázku
- Url
- OK
- Nastavení Imgur
- Došlo k chybě při nahrávání na Imgur:
- Nahrát na Imgur
- Obrázek byl úspěšně odeslán na Imgur!
- Použijte odkaz na stránku namísto odkaz na obrázek do schránky
-
+
+
+
+ Použít anonymní přístup
+ Zrušit
+ Jste si jisti, že chcete smazat místní historii Imgur?
+ Komunikace s Imgur. Prosím čekejte ...
+ Nastavení
+ Jste si jisti, že chcete smazat obrázek {0} z Imgur?
+ Odstranit Imgur {0}
+ Historie
+ Formát obrázku
+ Url
+ OK
+ Nastavení Imgur
+ Došlo k chybě při nahrávání na Imgur:
+ Nahrát na Imgur
+ Obrázek byl úspěšně odeslán na Imgur!
+ Použijte odkaz na stránku namísto odkaz na obrázek do schránky
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-de-DE.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-de-DE.xml
similarity index 96%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-de-DE.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-de-DE.xml
index 2fc7ffd06..a4c50d955 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-de-DE.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-de-DE.xml
@@ -1,50 +1,50 @@
-
-
-
-
- Zu Imgur hochladen
-
-
- Imgur Einstellungen
-
-
- Url
-
-
- OK
-
-
- Cancel
-
-
- Das Hochladen zu Imgur war erfolgreich.
-
-
- Es gab einen Fehler beim Hochladen zu Imgur:
-
-
- Bildformat
-
-
- Übermittle Daten zu Imgur. Bitte warten...
-
-
- Sind Sie sicher das sie das Bild {0} von Imgur löschen möchte?
-
-
- Sind Sie sicher das sie den lokalen Imgur Verlauf löschen möchte?
-
-
- Imgur {0} löschen
-
-
- Benutze der Seite-URL statt Bild-URL im Zwischenablage
-
-
- Verlauf
-
-
- Einstellungen
-
-
+
+
+
+
+ Zu Imgur hochladen
+
+
+ Imgur Einstellungen
+
+
+ Url
+
+
+ OK
+
+
+ Cancel
+
+
+ Das Hochladen zu Imgur war erfolgreich.
+
+
+ Es gab einen Fehler beim Hochladen zu Imgur:
+
+
+ Bildformat
+
+
+ Übermittle Daten zu Imgur. Bitte warten...
+
+
+ Sind Sie sicher das sie das Bild {0} von Imgur löschen möchte?
+
+
+ Sind Sie sicher das sie den lokalen Imgur Verlauf löschen möchte?
+
+
+ Imgur {0} löschen
+
+
+ Benutze der Seite-URL statt Bild-URL im Zwischenablage
+
+
+ Verlauf
+
+
+ Einstellungen
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-en-US.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-en-US.xml
similarity index 92%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-en-US.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-en-US.xml
index aa9883ef9..22eb3840e 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-en-US.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-en-US.xml
@@ -1,53 +1,53 @@
-
-
-
-
- Upload to Imgur
-
-
- Imgur settings
-
-
- Url
-
-
- OK
-
-
- Cancel
-
-
- Successfully uploaded image to Imgur!
-
-
- An error occurred while uploading to Imgur:
-
-
- Image format
-
-
- Communicating with Imgur. Please wait...
-
-
- Are you sure you want to delete the image {0} from Imgur?
-
-
- Are you sure you want to delete the local Imgur history?
-
-
- Delete Imgur {0}
-
-
- Use anonymous access
-
-
- Use page link instead of image link on clipboard
-
-
- History
-
-
- Configure
-
-
+
+
+
+
+ Upload to Imgur
+
+
+ Imgur settings
+
+
+ Url
+
+
+ OK
+
+
+ Cancel
+
+
+ Successfully uploaded image to Imgur!
+
+
+ An error occured while uploading to Imgur:
+
+
+ Image format
+
+
+ Communicating with Imgur. Please wait...
+
+
+ Are you sure you want to delete the image {0} from Imgur?
+
+
+ Are you sure you want to delete the local Imgur history?
+
+
+ Delete Imgur {0}
+
+
+ Use anonymous access
+
+
+ Use page link instead of image link on clipboard
+
+
+ History
+
+
+ Configure
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-fr-FR.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-fr-FR.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-fr-FR.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-fr-FR.xml
index 578c10215..29f191e67 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-fr-FR.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-fr-FR.xml
@@ -1,20 +1,20 @@
-
-
-
- Annuler
- Êtes-vous sûr(e) de vouloir supprimer l'historique local de Imgur ?
- Communication en cours avec Imgur. Veuillez patienter...
- Configurer
- Êtes-vous sûr(e) de vouloir supprimer l'image {0} de Imgur ?
- Supprimer Imgur {0}
- Historique
- Format image
- Url
- OK
- Paramètres Imgur
- Une erreur est survenue lors du téléversement vers Imgur :
- Téléverser vers Imgur
- Image téléversée vers Imgur avec succès !
- Utiliser le lien de la page au lieu du lien de l'image dans le presse-papier
-
+
+
+
+ Annuler
+ Êtes-vous sûr(e) de vouloir supprimer l'historique local de Imgur ?
+ Communication en cours avec Imgur. Veuillez patienter...
+ Configurer
+ Êtes-vous sûr(e) de vouloir supprimer l'image {0} de Imgur ?
+ Supprimer Imgur {0}
+ Historique
+ Format image
+ Url
+ OK
+ Paramètres Imgur
+ Une erreur est survenue lors du téléversement vers Imgur :
+ Téléverser vers Imgur
+ Image téléversée vers Imgur avec succès !
+ Utiliser le lien de la page au lieu du lien de l'image dans le presse-papier
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-id-ID.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-id-ID.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-id-ID.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-id-ID.xml
index 43823cf71..c73051357 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-id-ID.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-id-ID.xml
@@ -1,21 +1,21 @@
-
-
-
- Gunakan akses anonim
- Batal
- Apakah anda benar-benar ingin menghapus riwayat lokal Imgur?
- Menyambungkan ke Imgur. Tunggu sebentar...
- Pengaturan
- Apakah anda benar-benar ingin menghapus gambar {0} dari Imgur?
- Hapus Imgur {0}
- Riwayat
- Format gambar
- Url
- Oke
- Setelan Imgur
- Kesalahan terjadi ketika mengunggah ke Imgur:
- Unggah ke Imgur
- Gambar berhasil diunggah ke Imgur!
- Gunakan link laman daripada link gambar di papanklip
-
+
+
+
+ Gunakan akses anonim
+ Batal
+ Apakah anda benar-benar ingin menghapus riwayat lokal Imgur?
+ Menyambungkan ke Imgur. Tunggu sebentar...
+ Pengaturan
+ Apakah anda benar-benar ingin menghapus gambar {0} dari Imgur?
+ Hapus Imgur {0}
+ Riwayat
+ Format gambar
+ Url
+ Oke
+ Setelan Imgur
+ Kesalahan terjadi ketika mengunggah ke Imgur:
+ Unggah ke Imgur
+ Gambar berhasil diunggah ke Imgur!
+ Gunakan link laman daripada link gambar di papanklip
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-it-IT.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-it-IT.xml
similarity index 64%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-it-IT.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-it-IT.xml
index b0d86a5d0..f3bb72df1 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-it-IT.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-it-IT.xml
@@ -1,53 +1,50 @@
-
-
-
-
- Carica su Imgur
-
-
- Impostazioni Imgur
-
-
- URL
-
-
- OK
-
-
- Annulla
-
-
- caricamento immagine su Imgur completato!
-
-
- Si è verificato un problema durante il caricamento:
-
-
- Formato immagine
-
-
- Comunicazione con Imgur...
-
-
- Sei sicuro di voler eliminare l'immagine {0} da Imgur?
-
-
- Sei sicuro di voler eliminare la cronologia locale di Imgur?
-
-
- Elimina Imgur {0}
-
-
- Usa accesso anonimo
-
-
- Negli Appunti usa collegamento pagina invece di quello all'immagine
-
-
- Cronologia
-
-
- Impostazioni
-
-
+
+
+
+
+ Carica su Imgur
+
+
+ Impostazioni Imgur
+
+
+ Url
+
+
+ OK
+
+
+ Annulla
+
+
+ Immagine caricata correttamente su Imgur!
+
+
+ Si è verificato un problema durante il collegamento:
+
+
+ Formato immagine
+
+
+ Comunicazione con Imgur. Attendere prego...
+
+
+ Sei sicuro si voler eliminare l'immagine {0} da Imgur?
+
+
+ Sei sicuro si voler eliminare la cronologia locale di Imgur?
+
+
+ Elimina Imgur {0}
+
+
+ Usa il collegamento alla pagina invece di quello all'immagine su appunti
+
+
+ Cronologia
+
+
+ Configurazione
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-ja-JP.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-ja-JP.xml
similarity index 100%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-ja-JP.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-ja-JP.xml
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-kab-DZ.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-kab-DZ.xml
similarity index 100%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-kab-DZ.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-kab-DZ.xml
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-ko-KR.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-ko-KR.xml
similarity index 100%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-ko-KR.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-ko-KR.xml
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-lv-LV.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-lv-LV.xml
similarity index 100%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-lv-LV.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-lv-LV.xml
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-nl-NL.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-nl-NL.xml
similarity index 96%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-nl-NL.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-nl-NL.xml
index 9550bd2b5..a3692776e 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-nl-NL.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-nl-NL.xml
@@ -1,50 +1,50 @@
-
-
-
-
- Naar Imgur uploaden
-
-
- Imgur instellingen
-
-
- Url
-
-
- OK
-
-
- Afbreken
-
-
- Het uploaden naar Imgur is geslaagt!
-
-
- Tijdens het uploaden naar Imgur is een fout opgetreden:
-
-
- Beeld formaat
-
-
- Data overdraging naar Imgur, wachten AUB...
-
-
- Weet U zeker dat U het beeld {0} van Imgur verwijderen wilt?
-
-
- Weet U zeker dat U de locale Imgur historie verwijderen wilt?
-
-
- Imgur {0} verwijderen
-
-
- Kopieer de pagina link in plaats van de beeld link in het klembord
-
-
- Verloop
-
-
- Instellingen
-
-
+
+
+
+
+ Naar Imgur uploaden
+
+
+ Imgur instellingen
+
+
+ Url
+
+
+ OK
+
+
+ Afbreken
+
+
+ Het uploaden naar Imgur is geslaagt!
+
+
+ Tijdens het uploaden naar Imgur is een fout opgetreden:
+
+
+ Beeld formaat
+
+
+ Data overdraging naar Imgur, wachten AUB...
+
+
+ Weet U zeker dat U het beeld {0} van Imgur verwijderen wilt?
+
+
+ Weet U zeker dat U de locale Imgur historie verwijderen wilt?
+
+
+ Imgur {0} verwijderen
+
+
+ Kopieer de pagina link in plaats van de beeld link in het klembord
+
+
+ Verloop
+
+
+ Instellingen
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-pl-PL.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-pl-PL.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-pl-PL.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-pl-PL.xml
index e2b6caa39..7db3a2118 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-pl-PL.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-pl-PL.xml
@@ -1,21 +1,21 @@
-
-
-
- Używaj jako niezalogowany
- Anuluj
- Na pewno chcesz usunąć historię lokalną Imgur?
- Trwa komunikacja z Imgur. Proszę czekać...
- Konfiguruj
- Na pewno chcesz usunąć obraz {0} z Imgur?
- Usuń z Imgur {0}
- Historia
- Format obrazów
- URL
- OK
- Ustawienia Imgur
- Wystąpił błąd przy wysyłaniu do Imgur:
- Wyślij do Imgur
- Wysyłanie obrazu do Imgur powiodło się!
- Kopiuj do schowka link do strony zamiast link do obrazu
-
+
+
+
+ Używaj jako niezalogowany
+ Anuluj
+ Na pewno chcesz usunąć historię lokalną Imgur?
+ Trwa komunikacja z Imgur. Proszę czekać...
+ Konfiguruj
+ Na pewno chcesz usunąć obraz {0} z Imgur?
+ Usuń z Imgur {0}
+ Historia
+ Format obrazów
+ URL
+ OK
+ Ustawienia Imgur
+ Wystąpił błąd przy wysyłaniu do Imgur:
+ Wyślij do Imgur
+ Wysyłanie obrazu do Imgur powiodło się!
+ Kopiuj do schowka link do strony zamiast link do obrazu
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-pt-PT.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-pt-PT.xml
similarity index 100%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-pt-PT.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-pt-PT.xml
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-ru-RU.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-ru-RU.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-ru-RU.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-ru-RU.xml
index 2279b6c4f..719c49b3f 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-ru-RU.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-ru-RU.xml
@@ -1,21 +1,21 @@
-
-
-
- Использовать анонимный доступ
- Отмена
- Вы действительно хотите удалить местную историю Imgur?
- Взаимодействие с Imgur. Подождите...
- Настройка
- Вы действительно хотите удалить изображение {0} из Imgur?
- Удалить Imgur {0}
- История
- Формат изображения
- Url
- OK
- Imgur параметры
- Произошла ошибка при загрузке на Imgur:
- Загрузить на Imgur
- Изображение успешно загружено на Imgur!
- Использовать ссылку страницы, вместо ссылки изображения в буфере обмена
-
+
+
+
+ Использовать анонимный доступ
+ Отмена
+ Вы действительно хотите удалить местную историю Imgur?
+ Взаимодействие с Imgur. Подождите...
+ Настройка
+ Вы действительно хотите удалить изображение {0} из Imgur?
+ Удалить Imgur {0}
+ История
+ Формат изображения
+ Url
+ OK
+ Imgur параметры
+ Произошла ошибка при загрузке на Imgur:
+ Загрузить на Imgur
+ Изображение успешно загружено на Imgur!
+ Использовать ссылку страницы, вместо ссылки изображения в буфере обмена
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-sk-SK.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-sk-SK.xml
similarity index 96%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-sk-SK.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-sk-SK.xml
index bb08ee691..bc4bcfb82 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-sk-SK.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-sk-SK.xml
@@ -1,53 +1,53 @@
-
-
-
-
- Nahrať na Imgur
-
-
- Imgur nastavenia
-
-
- Url
-
-
- OK
-
-
- Zrušiť
-
-
- Obrázok bol úspešne nahraný na Imgur!
-
-
- Chyba pri nahrávaní na Imgur:
-
-
- Formát obrázku
-
-
- Prebieha komunikácia s Imgur. Čakajte prosím ...
-
-
- Chcete naozaj zmazať obrázok {0} z Imgur?
-
-
- Chcete naozaj zmazať lokálnu históriu Imgur?
-
-
- Zmazať Imgur {0}
-
-
- Použiť anonymný prístup
-
-
- Použiť odkaz stránky namiesto odkazu obrázku v schránke
-
-
- História
-
-
- Konfigurácia
-
-
+
+
+
+
+ Nahrať na Imgur
+
+
+ Imgur nastavenia
+
+
+ Url
+
+
+ OK
+
+
+ Zrušiť
+
+
+ Obrázok bol úspešne nahraný na Imgur!
+
+
+ Chyba pri nahrávaní na Imgur:
+
+
+ Formát obrázku
+
+
+ Prebieha komunikácia s Imgur. Čakajte prosím ...
+
+
+ Chcete naozaj zmazať obrázok {0} z Imgur?
+
+
+ Chcete naozaj zmazať lokálnu históriu Imgur?
+
+
+ Zmazať Imgur {0}
+
+
+ Použiť anonymný prístup
+
+
+ Použiť odkaz stránky namiesto odkazu obrázku v schránke
+
+
+ História
+
+
+ Konfigurácia
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-sr-RS.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-sr-RS.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-sr-RS.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-sr-RS.xml
index 521257ead..f1db2a0ba 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-sr-RS.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-sr-RS.xml
@@ -1,21 +1,21 @@
-
-
-
- Анонимни приступ
- Откажи
- Желите ли да обришете локалну историју Имиџера?
- Комуницирам с Имиџером. Сачекајте…
- Поставке
- Желите ли да обришете слику {0} са Имиџера?
- Обриши Имиџер {0}
- Историја
- Формат слике:
- Адреса
- У реду
- Поставке Имиџера
- Дошло је до грешке при отпремању на Имиџер:
- Отпреми на Имиџер
- Слика је успешно отпремљена на Имиџер.
- Веза ка страници уместо везе ка слици у остави
-
+
+
+
+ Анонимни приступ
+ Откажи
+ Желите ли да обришете локалну историју Имиџера?
+ Комуницирам с Имиџером. Сачекајте…
+ Поставке
+ Желите ли да обришете слику {0} са Имиџера?
+ Обриши Имиџер {0}
+ Историја
+ Формат слике:
+ Адреса
+ У реду
+ Поставке Имиџера
+ Дошло је до грешке при отпремању на Имиџер:
+ Отпреми на Имиџер
+ Слика је успешно отпремљена на Имиџер.
+ Веза ка страници уместо везе ка слици у остави
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-sv-SE.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-sv-SE.xml
similarity index 100%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-sv-SE.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-sv-SE.xml
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-uk-UA.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-uk-UA.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-uk-UA.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-uk-UA.xml
index df5ff014a..8152b94fe 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-uk-UA.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-uk-UA.xml
@@ -1,21 +1,21 @@
-
-
-
- Вивантажити на Imgur
- Параметри Imgur
- Посилання
- Гаразд
- Скасувати
- Зображення вдало вивантажено на Imgur!
- Відбулась помилка при вивантаженні зображення на Imgur:
- Формат зображення
- З’єднання з Imgur. Будь ласка, зачекайте...
- Чи дійсно Ви хочете видалити зображення {0} з Imgur?
- Чи дійсно Ви хочете видалити локальну історію Imgur?
- Видалити Imgur {0}
- Використовувати анонімний доступ
- Використовувати посилання на сторінку замість посилання на зображення
- Історія
- Налаштувати
-
-
+
+
+
+ Вивантажити на Imgur
+ Параметри Imgur
+ Посилання
+ Гаразд
+ Скасувати
+ Зображення вдало вивантажено на Imgur!
+ Відбулась помилка при вивантаженні зображення на Imgur:
+ Формат зображення
+ З’єднання з Imgur. Будь ласка, зачекайте...
+ Чи дійсно Ви хочете видалити зображення {0} з Imgur?
+ Чи дійсно Ви хочете видалити локальну історію Imgur?
+ Видалити Imgur {0}
+ Використовувати анонімний доступ
+ Використовувати посилання на сторінку замість посилання на зображення
+ Історія
+ Налаштувати
+
+
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-zh-CN.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-zh-CN.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-zh-CN.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-zh-CN.xml
index acb0b6426..ce0541241 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-zh-CN.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-zh-CN.xml
@@ -1,21 +1,21 @@
-
-
-
- 使用匿名连接
- 取消
- 是否真的要删除本地的Imgur历史记录?
- 正在连接到Imgur。请稍后...
- 配置
- 是否真的要从Imgur上删除图片{0}?
- 删除 Imgur {0}
- 历史记录
- 图片格式
- 网址
- 确认
- Imgur设置
- 上传图片到Imgur时发生错误:
- 上传到Imgur
- 图片已成功上传到了Imgur!
- 当复制链接到剪贴板时,使用页面的网址而不是图片的网址
-
+
+
+
+ 使用匿名连接
+ 取消
+ 是否真的要删除本地的Imgur历史记录?
+ 正在连接到Imgur。请稍后...
+ 配置
+ 是否真的要从Imgur上删除图片{0}?
+ 删除 Imgur {0}
+ 历史记录
+ 图片格式
+ 网址
+ 确认
+ Imgur设置
+ 上传图片到Imgur时发生错误:
+ 上传到Imgur
+ 图片已成功上传到了Imgur!
+ 当复制链接到剪贴板时,使用页面的网址而不是图片的网址
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-zh-TW.xml b/GreenshotImgurPlugin/Languages/language_imgurplugin-zh-TW.xml
similarity index 98%
rename from src/Greenshot.Plugin.Imgur/Languages/language_imgur-zh-TW.xml
rename to GreenshotImgurPlugin/Languages/language_imgurplugin-zh-TW.xml
index 5a743870a..1fe08f257 100644
--- a/src/Greenshot.Plugin.Imgur/Languages/language_imgur-zh-TW.xml
+++ b/GreenshotImgurPlugin/Languages/language_imgurplugin-zh-TW.xml
@@ -1,21 +1,21 @@
-
-
-
- 使用匿名存取
- 取消
- 您確定要刪除本機 Imgur 歷程記錄嗎?
- 正在與 Imgur 通訊,請稍候...
- 組態
- 您確定要從 Imgur 刪除圖片 {0} 嗎?
- 刪除 Imgur {0}
- 歷程記錄
- 圖片格式
- URL
- 確定
- Imgur 設定
- 上傳到 Imgur 時發生錯誤:
- 上傳到 Imgur
- 上傳圖片到 Imgur 成功!
- 在剪貼簿使用頁面連結而不是圖片連結
-
+
+
+
+ 使用匿名存取
+ 取消
+ 您確定要刪除本機 Imgur 歷程記錄嗎?
+ 正在與 Imgur 通訊,請稍候...
+ 組態
+ 您確定要從 Imgur 刪除圖片 {0} 嗎?
+ 刪除 Imgur {0}
+ 歷程記錄
+ 圖片格式
+ URL
+ 確定
+ Imgur 設定
+ 上傳到 Imgur 時發生錯誤:
+ 上傳到 Imgur
+ 上傳圖片到 Imgur 成功!
+ 在剪貼簿使用頁面連結而不是圖片連結
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs b/GreenshotImgurPlugin/Properties/AssemblyInfo.cs
similarity index 79%
rename from src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs
rename to GreenshotImgurPlugin/Properties/AssemblyInfo.cs
index 4f9d4afb5..19a2e1176 100644
--- a/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs
+++ b/GreenshotImgurPlugin/Properties/AssemblyInfo.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,19 +16,17 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
using System.Reflection;
using System.Runtime.InteropServices;
-using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to Imgur")]
-[assembly: AssemblyPluginIdentifier("Imgur Plugin")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
-[assembly: ComVisible(false)]
\ No newline at end of file
+[assembly: ComVisible(false)]
diff --git a/GreenshotJiraPlugin/AsyncMemoryCache.cs b/GreenshotJiraPlugin/AsyncMemoryCache.cs
new file mode 100644
index 000000000..0242573e3
--- /dev/null
+++ b/GreenshotJiraPlugin/AsyncMemoryCache.cs
@@ -0,0 +1,236 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Runtime.Caching;
+using System.Threading;
+using System.Threading.Tasks;
+using Dapplo.Log;
+
+
+namespace GreenshotJiraPlugin
+{
+ ///
+ /// This abstract class builds a base for a simple async memory cache.
+ ///
+ /// Type for the key
+ /// Type for the stored value
+ public abstract class AsyncMemoryCache where TResult : class
+ {
+ private static readonly Task EmptyValueTask = Task.FromResult(null);
+ private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
+ private readonly MemoryCache _cache = new MemoryCache(Guid.NewGuid().ToString());
+ private readonly LogSource _log = new LogSource();
+
+ ///
+ /// Set the timespan for items to expire.
+ ///
+ public TimeSpan? ExpireTimeSpan { get; set; }
+
+ ///
+ /// Set the timespan for items to slide.
+ ///
+ public TimeSpan? SlidingTimeSpan { get; set; }
+
+ ///
+ /// Specifies if the RemovedCallback needs to be called
+ /// If this is active, ActivateUpdateCallback should be false
+ ///
+ protected bool ActivateRemovedCallback { get; set; } = true;
+
+ ///
+ /// Specifies if the UpdateCallback needs to be called.
+ /// If this is active, ActivateRemovedCallback should be false
+ ///
+ protected bool ActivateUpdateCallback { get; set; } = false;
+
+ ///
+ /// Implement this method, it should create an instance of TResult via the supplied TKey.
+ ///
+ /// TKey
+ /// CancellationToken
+ /// TResult
+ protected abstract Task CreateAsync(TKey key, CancellationToken cancellationToken = default);
+
+ ///
+ /// Creates a key under which the object is stored or retrieved, default is a toString on the object.
+ ///
+ /// TKey
+ /// string
+ protected virtual string CreateKey(TKey keyObject)
+ {
+ return keyObject.ToString();
+ }
+
+ ///
+ /// Get an element from the cache, if this is not available call the create function.
+ ///
+ /// object for the key
+ /// CancellationToken
+ /// TResult
+ public async Task DeleteAsync(TKey keyObject, CancellationToken cancellationToken = default)
+ {
+ var key = CreateKey(keyObject);
+ await _semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false);
+ try
+ {
+ _cache.Remove(key);
+ }
+ finally
+ {
+ _semaphoreSlim.Release();
+ }
+ }
+
+ ///
+ /// Get a task element from the cache, if this is not available return null.
+ /// You probably want to call GetOrCreateAsync
+ ///
+ /// object for the key
+ /// Task with TResult, null if no value
+ public Task GetAsync(TKey keyObject)
+ {
+ var key = CreateKey(keyObject);
+ return _cache.Get(key) as Task ?? EmptyValueTask;
+ }
+
+ ///
+ /// Get a task element from the cache, if this is not available call the create function.
+ ///
+ /// object for the key
+ /// CancellationToken
+ /// Task with TResult
+ public Task GetOrCreateAsync(TKey keyObject, CancellationToken cancellationToken = default)
+ {
+ var key = CreateKey(keyObject);
+ return _cache.Get(key) as Task ?? GetOrCreateInternalAsync(keyObject, null, cancellationToken);
+ }
+
+ ///
+ /// Get a task element from the cache, if this is not available call the create function.
+ ///
+ /// object for the key
+ /// CacheItemPolicy for when you want more control over the item
+ /// CancellationToken
+ /// Task with TResult
+ public Task GetOrCreateAsync(TKey keyObject, CacheItemPolicy cacheItemPolicy, CancellationToken cancellationToken = default)
+ {
+ var key = CreateKey(keyObject);
+ return _cache.Get(key) as Task ?? GetOrCreateInternalAsync(keyObject, cacheItemPolicy, cancellationToken);
+ }
+
+ ///
+ /// This takes care of the real async part of the code.
+ ///
+ ///
+ /// CacheItemPolicy for when you want more control over the item
+ /// CancellationToken
+ /// TResult
+ private async Task GetOrCreateInternalAsync(TKey keyObject, CacheItemPolicy cacheItemPolicy = null, CancellationToken cancellationToken = default)
+ {
+ var key = CreateKey(keyObject);
+ var completionSource = new TaskCompletionSource();
+
+ if (cacheItemPolicy == null)
+ {
+ cacheItemPolicy = new CacheItemPolicy
+ {
+ AbsoluteExpiration = ExpireTimeSpan.HasValue ? DateTimeOffset.Now.Add(ExpireTimeSpan.Value) : ObjectCache.InfiniteAbsoluteExpiration,
+ SlidingExpiration = SlidingTimeSpan ?? ObjectCache.NoSlidingExpiration
+ };
+ if (ActivateUpdateCallback)
+ {
+ cacheItemPolicy.UpdateCallback = UpdateCallback;
+ }
+ if (ActivateRemovedCallback)
+ {
+ cacheItemPolicy.RemovedCallback = RemovedCallback;
+ }
+ }
+
+ // Test if we got an existing object or our own
+ if (_cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) is Task result && !completionSource.Task.Equals(result))
+ {
+ return await result.ConfigureAwait(false);
+ }
+
+ await _semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false);
+ try
+ {
+ result = _cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) as Task;
+ if (result != null && !completionSource.Task.Equals(result))
+ {
+ return await result.ConfigureAwait(false);
+ }
+
+ // Now, start the background task, which will set the completionSource with the correct response
+ // ReSharper disable once MethodSupportsCancellation
+ // ReSharper disable once UnusedVariable
+ var ignoreBackgroundTask = Task.Run(async () =>
+ {
+ try
+ {
+ var backgroundResult = await CreateAsync(keyObject, cancellationToken).ConfigureAwait(false);
+ completionSource.TrySetResult(backgroundResult);
+ }
+ catch (TaskCanceledException)
+ {
+ completionSource.TrySetCanceled();
+ }
+ catch (Exception ex)
+ {
+ completionSource.TrySetException(ex);
+ }
+ });
+ }
+ finally
+ {
+ _semaphoreSlim.Release();
+ }
+
+ return await completionSource.Task.ConfigureAwait(false);
+ }
+
+ ///
+ /// Override to know when an item is removed, make sure to configure ActivateUpdateCallback / ActivateRemovedCallback
+ ///
+ /// CacheEntryRemovedArguments
+ protected virtual void RemovedCallback(CacheEntryRemovedArguments cacheEntryRemovedArguments)
+ {
+ _log.Verbose().WriteLine("Item {0} removed due to {1}.", cacheEntryRemovedArguments.CacheItem.Key, cacheEntryRemovedArguments.RemovedReason);
+ if (cacheEntryRemovedArguments.CacheItem.Value is IDisposable disposable)
+ {
+ _log.Debug().WriteLine("Disposed cached item.");
+ disposable.Dispose();
+ }
+ }
+
+ ///
+ /// Override to modify the cache behaviour when an item is about to be removed, make sure to configure
+ /// ActivateUpdateCallback / ActivateRemovedCallback
+ ///
+ /// CacheEntryUpdateArguments
+ protected virtual void UpdateCallback(CacheEntryUpdateArguments cacheEntryUpdateArguments)
+ {
+ _log.Verbose().WriteLine("Update request for {0} due to {1}.", cacheEntryUpdateArguments.Key, cacheEntryUpdateArguments.RemovedReason);
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Jira/Forms/JiraForm.Designer.cs b/GreenshotJiraPlugin/Forms/JiraForm.Designer.cs
similarity index 93%
rename from src/Greenshot.Plugin.Jira/Forms/JiraForm.Designer.cs
rename to GreenshotJiraPlugin/Forms/JiraForm.Designer.cs
index c233ab6f1..12e85ca40 100644
--- a/src/Greenshot.Plugin.Jira/Forms/JiraForm.Designer.cs
+++ b/GreenshotJiraPlugin/Forms/JiraForm.Designer.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,9 +16,9 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
-namespace Greenshot.Plugin.Jira.Forms {
+namespace GreenshotJiraPlugin.Forms {
partial class JiraForm {
///
/// Required designer variable.
@@ -71,6 +71,7 @@ namespace Greenshot.Plugin.Jira.Forms {
//
// label_jirafilter
//
+ this.label_jirafilter.AutoSize = true;
this.label_jirafilter.Location = new System.Drawing.Point(14, 14);
this.label_jirafilter.Name = "label_jirafilter";
this.label_jirafilter.Size = new System.Drawing.Size(52, 13);
@@ -79,6 +80,7 @@ namespace Greenshot.Plugin.Jira.Forms {
//
// label_jira
//
+ this.label_jira.AutoSize = true;
this.label_jira.Location = new System.Drawing.Point(14, 41);
this.label_jira.Name = "label_jira";
this.label_jira.Size = new System.Drawing.Size(30, 13);
@@ -125,6 +127,7 @@ namespace Greenshot.Plugin.Jira.Forms {
// label_filename
//
this.label_filename.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label_filename.AutoSize = true;
this.label_filename.Location = new System.Drawing.Point(14, 222);
this.label_filename.Name = "label_filename";
this.label_filename.Size = new System.Drawing.Size(49, 13);
@@ -134,6 +137,7 @@ namespace Greenshot.Plugin.Jira.Forms {
// label_comment
//
this.label_comment.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label_comment.AutoSize = true;
this.label_comment.Location = new System.Drawing.Point(14, 248);
this.label_comment.Name = "label_comment";
this.label_comment.Size = new System.Drawing.Size(51, 13);
@@ -163,6 +167,7 @@ namespace Greenshot.Plugin.Jira.Forms {
// label1
//
this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(14, 274);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(23, 13);
diff --git a/GreenshotJiraPlugin/Forms/JiraForm.cs b/GreenshotJiraPlugin/Forms/JiraForm.cs
new file mode 100644
index 000000000..551fadadc
--- /dev/null
+++ b/GreenshotJiraPlugin/Forms/JiraForm.cs
@@ -0,0 +1,236 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Windows.Forms;
+using Dapplo.Jira.Entities;
+using GreenshotPlugin.Controls;
+using GreenshotPlugin.Core;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Threading.Tasks;
+using GreenshotPlugin.IniFile;
+
+namespace GreenshotJiraPlugin.Forms {
+ public partial class JiraForm : Form {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraForm));
+ private readonly JiraConnector _jiraConnector;
+ private Issue _selectedIssue;
+ private readonly GreenshotColumnSorter _columnSorter;
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+
+ public JiraForm(JiraConnector jiraConnector) {
+ InitializeComponent();
+ Icon = GreenshotResources.GetGreenshotIcon();
+ AcceptButton = uploadButton;
+ CancelButton = cancelButton;
+
+ InitializeComponentText();
+
+ _columnSorter = new GreenshotColumnSorter();
+ jiraListView.ListViewItemSorter = _columnSorter;
+
+ _jiraConnector = jiraConnector;
+
+ ChangeModus(false);
+
+ uploadButton.Enabled = false;
+ Load += OnLoad;
+ }
+
+ private async void OnLoad(object sender, EventArgs eventArgs)
+ {
+ try
+ {
+ if (!_jiraConnector.IsLoggedIn)
+ {
+ await _jiraConnector.LoginAsync();
+ }
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(Language.GetFormattedString("jira", LangKey.login_error, e.Message));
+ }
+ if (_jiraConnector.IsLoggedIn)
+ {
+ var filters = await _jiraConnector.GetFavoriteFiltersAsync();
+ if (filters.Count > 0)
+ {
+ foreach (var filter in filters)
+ {
+ jiraFilterBox.Items.Add(filter);
+ }
+ jiraFilterBox.SelectedIndex = 0;
+ }
+ ChangeModus(true);
+ if (_jiraConnector.Monitor.RecentJiras.Any())
+ {
+ _selectedIssue = _jiraConnector.Monitor.RecentJiras.First().JiraIssue;
+ jiraKey.Text = _selectedIssue.Key;
+ uploadButton.Enabled = true;
+ }
+ }
+ }
+
+ private void InitializeComponentText() {
+ label_jirafilter.Text = Language.GetString("jira", LangKey.label_jirafilter);
+ label_comment.Text = Language.GetString("jira", LangKey.label_comment);
+ label_filename.Text = Language.GetString("jira", LangKey.label_filename);
+ }
+
+ private void ChangeModus(bool enabled) {
+ jiraFilterBox.Enabled = enabled;
+ jiraListView.Enabled = enabled;
+ jiraFilenameBox.Enabled = enabled;
+ jiraCommentBox.Enabled = enabled;
+ }
+
+ public void SetFilename(string filename) {
+ jiraFilenameBox.Text = filename;
+ }
+
+ public Issue GetJiraIssue() {
+ return _selectedIssue;
+ }
+
+ public async Task UploadAsync(IBinaryContainer attachment) {
+ attachment.Filename = jiraFilenameBox.Text;
+ await _jiraConnector.AttachAsync(_selectedIssue.Key, attachment);
+
+ if (!string.IsNullOrEmpty(jiraCommentBox.Text)) {
+ await _jiraConnector.AddCommentAsync(_selectedIssue.Key, jiraCommentBox.Text);
+ }
+ }
+
+ private async void JiraFilterBox_SelectedIndexChanged(object sender, EventArgs e) {
+ if (_jiraConnector.IsLoggedIn) {
+
+ uploadButton.Enabled = false;
+ var filter = (Filter)jiraFilterBox.SelectedItem;
+ if (filter == null) {
+ return;
+ }
+ IList issues = null;
+ try
+ {
+ issues = await _jiraConnector.SearchAsync(filter);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex);
+ MessageBox.Show(this, ex.Message, "Error in filter", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+
+ jiraListView.Items.Clear();
+ if (issues?.Count > 0) {
+ jiraListView.Columns.Clear();
+ LangKey[] columns = { LangKey.column_issueType, LangKey.column_id, LangKey.column_created, LangKey.column_assignee, LangKey.column_reporter, LangKey.column_summary };
+ foreach (LangKey column in columns)
+ {
+ if (!Language.TryGetString("jira", column, out var translation))
+ {
+ translation = string.Empty;
+ }
+ jiraListView.Columns.Add(translation);
+ }
+ var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, DpiHelper.GetDpi(Handle));
+ var imageList = new ImageList {
+ ImageSize = scaledIconSize
+ };
+ jiraListView.SmallImageList = imageList;
+ jiraListView.LargeImageList = imageList;
+
+ foreach (var issue in issues) {
+ var item = new ListViewItem
+ {
+ Tag = issue
+ };
+ try
+ {
+ var issueIcon = await _jiraConnector.GetIssueTypeBitmapAsync(issue);
+ imageList.Images.Add(issueIcon);
+ item.ImageIndex = imageList.Images.Count - 1;
+ }
+ catch (Exception ex)
+ {
+ Log.Warn("Problem loading issue type, ignoring", ex);
+ }
+
+ item.SubItems.Add(issue.Key);
+ item.SubItems.Add(issue.Fields.Created.HasValue
+ ? issue.Fields.Created.Value.ToString("d", DateTimeFormatInfo.InvariantInfo)
+ : string.Empty);
+ item.SubItems.Add(issue.Fields.Assignee?.DisplayName);
+ item.SubItems.Add(issue.Fields.Reporter?.DisplayName);
+ item.SubItems.Add(issue.Fields.Summary);
+ jiraListView.Items.Add(item);
+ for (int i = 0; i < columns.Length; i++)
+ {
+ jiraListView.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent);
+ }
+ jiraListView.Invalidate();
+ jiraListView.Update();
+ }
+
+ jiraListView.Refresh();
+ }
+ }
+ }
+
+ private void JiraListView_SelectedIndexChanged(object sender, EventArgs e) {
+ if (jiraListView.SelectedItems.Count > 0) {
+ _selectedIssue = (Issue)jiraListView.SelectedItems[0].Tag;
+ jiraKey.Text = _selectedIssue.Key;
+ uploadButton.Enabled = true;
+ } else {
+ uploadButton.Enabled = false;
+ }
+ }
+
+ private void JiraListView_ColumnClick(object sender, ColumnClickEventArgs e) {
+ // Determine if clicked column is already the column that is being sorted.
+ if (e.Column == _columnSorter.SortColumn) {
+ // Reverse the current sort direction for this column.
+ _columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
+ } else {
+ // Set the column number that is to be sorted; default to ascending.
+ _columnSorter.SortColumn = e.Column;
+ _columnSorter.Order = SortOrder.Ascending;
+ }
+
+ // Perform the sort with these new sort options.
+ jiraListView.Sort();
+ }
+
+ private async void JiraKeyTextChanged(object sender, EventArgs e) {
+ string jiranumber = jiraKey.Text;
+ uploadButton.Enabled = false;
+ int dashIndex = jiranumber.IndexOf('-');
+ if (dashIndex > 0 && jiranumber.Length > dashIndex+1) {
+ _selectedIssue = await _jiraConnector.GetIssueAsync(jiraKey.Text);
+ if (_selectedIssue != null) {
+ uploadButton.Enabled = true;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Greenshot.Editor/Helpers/IDoubleProcessor.cs b/GreenshotJiraPlugin/Forms/JiraFormBase.cs
similarity index 69%
rename from src/Greenshot.Editor/Helpers/IDoubleProcessor.cs
rename to GreenshotJiraPlugin/Forms/JiraFormBase.cs
index beb426c68..47266fda8 100644
--- a/src/Greenshot.Editor/Helpers/IDoubleProcessor.cs
+++ b/GreenshotJiraPlugin/Forms/JiraFormBase.cs
@@ -1,8 +1,8 @@
-/*
+/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,13 +16,12 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
-namespace Greenshot.Editor.Helpers
-{
- public interface IDoubleProcessor
- {
- double Process(double d);
- }
-}
\ No newline at end of file
+using GreenshotPlugin.Controls;
+
+namespace GreenshotJiraPlugin.Forms {
+ public class JiraFormBase : GreenshotForm {
+ }
+}
diff --git a/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs b/GreenshotJiraPlugin/Forms/SettingsForm.Designer.cs
similarity index 79%
rename from src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs
rename to GreenshotJiraPlugin/Forms/SettingsForm.Designer.cs
index 6903f74fd..e3f26b571 100644
--- a/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs
+++ b/GreenshotJiraPlugin/Forms/SettingsForm.Designer.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,12 +16,9 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
-
-using Greenshot.Base.Controls;
-
-namespace Greenshot.Plugin.Jira.Forms {
+namespace GreenshotJiraPlugin.Forms {
partial class SettingsForm {
///
/// Designer variable used to keep track of non-visual components.
@@ -49,12 +46,12 @@ namespace Greenshot.Plugin.Jira.Forms {
///
private void InitializeComponent()
{
- this.buttonOK = new GreenshotButton();
- this.buttonCancel = new GreenshotButton();
- this.label_url = new GreenshotLabel();
- this.textBoxUrl = new GreenshotTextBox();
- this.combobox_uploadimageformat = new GreenshotComboBox();
- this.label_upload_format = new GreenshotLabel();
+ this.buttonOK = new GreenshotPlugin.Controls.GreenshotButton();
+ this.buttonCancel = new GreenshotPlugin.Controls.GreenshotButton();
+ this.label_url = new GreenshotPlugin.Controls.GreenshotLabel();
+ this.textBoxUrl = new GreenshotPlugin.Controls.GreenshotTextBox();
+ this.combobox_uploadimageformat = new GreenshotPlugin.Controls.GreenshotComboBox();
+ this.label_upload_format = new GreenshotPlugin.Controls.GreenshotLabel();
this.SuspendLayout();
//
// buttonOK
@@ -94,7 +91,7 @@ namespace Greenshot.Plugin.Jira.Forms {
this.textBoxUrl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.textBoxUrl.Location = new System.Drawing.Point(164, 21);
this.textBoxUrl.Name = "textBoxUrl";
- this.textBoxUrl.PropertyName = nameof(JiraConfiguration.Url);
+ this.textBoxUrl.PropertyName = "Url";
this.textBoxUrl.SectionName = "Jira";
this.textBoxUrl.Size = new System.Drawing.Size(214, 20);
this.textBoxUrl.TabIndex = 6;
@@ -105,7 +102,7 @@ namespace Greenshot.Plugin.Jira.Forms {
this.combobox_uploadimageformat.FormattingEnabled = true;
this.combobox_uploadimageformat.Location = new System.Drawing.Point(164, 47);
this.combobox_uploadimageformat.Name = "combobox_uploadimageformat";
- this.combobox_uploadimageformat.PropertyName = nameof(JiraConfiguration.UploadFormat);
+ this.combobox_uploadimageformat.PropertyName = "UploadFormat";
this.combobox_uploadimageformat.SectionName = "Jira";
this.combobox_uploadimageformat.Size = new System.Drawing.Size(214, 21);
this.combobox_uploadimageformat.TabIndex = 8;
@@ -139,11 +136,11 @@ namespace Greenshot.Plugin.Jira.Forms {
this.PerformLayout();
}
- private GreenshotComboBox combobox_uploadimageformat;
- private GreenshotLabel label_upload_format;
- private GreenshotTextBox textBoxUrl;
- private GreenshotLabel label_url;
- private GreenshotButton buttonCancel;
- private GreenshotButton buttonOK;
+ private GreenshotPlugin.Controls.GreenshotComboBox combobox_uploadimageformat;
+ private GreenshotPlugin.Controls.GreenshotLabel label_upload_format;
+ private GreenshotPlugin.Controls.GreenshotTextBox textBoxUrl;
+ private GreenshotPlugin.Controls.GreenshotLabel label_url;
+ private GreenshotPlugin.Controls.GreenshotButton buttonCancel;
+ private GreenshotPlugin.Controls.GreenshotButton buttonOK;
}
}
diff --git a/GreenshotJiraPlugin/Forms/SettingsForm.cs b/GreenshotJiraPlugin/Forms/SettingsForm.cs
new file mode 100644
index 000000000..5599da8ac
--- /dev/null
+++ b/GreenshotJiraPlugin/Forms/SettingsForm.cs
@@ -0,0 +1,37 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotJiraPlugin.Forms {
+ ///
+ /// Description of PasswordRequestForm.
+ ///
+ public partial class SettingsForm : JiraFormBase {
+ public SettingsForm()
+ {
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+ AcceptButton = buttonOK;
+ CancelButton = buttonCancel;
+ }
+ }
+}
diff --git a/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj b/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj
new file mode 100644
index 000000000..5579b7dac
--- /dev/null
+++ b/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj
@@ -0,0 +1,17 @@
+
+
+
+ GreenshotJiraPlugin
+ GreenshotJiraPlugin
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GreenshotJiraPlugin/IssueTypeBitmapCache.cs b/GreenshotJiraPlugin/IssueTypeBitmapCache.cs
new file mode 100644
index 000000000..2edc0ed3e
--- /dev/null
+++ b/GreenshotJiraPlugin/IssueTypeBitmapCache.cs
@@ -0,0 +1,55 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Drawing;
+using System.Threading;
+using System.Threading.Tasks;
+using Dapplo.Jira;
+using Dapplo.Jira.Entities;
+
+namespace GreenshotJiraPlugin
+{
+ ///
+ /// This is the bach for the IssueType bitmaps
+ ///
+ public class IssueTypeBitmapCache : AsyncMemoryCache
+ {
+ private readonly IJiraClient _jiraClient;
+
+ public IssueTypeBitmapCache(IJiraClient jiraClient)
+ {
+ _jiraClient = jiraClient;
+ // Set the expire timeout to an hour
+ ExpireTimeSpan = TimeSpan.FromHours(4);
+ }
+
+ protected override string CreateKey(IssueType keyObject)
+ {
+ return keyObject.Name;
+ }
+
+ protected override async Task CreateAsync(IssueType issueType, CancellationToken cancellationToken = new CancellationToken())
+ {
+ return await _jiraClient.Server.GetUriContentAsync(issueType.IconUri, cancellationToken).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/GreenshotJiraPlugin/JiraConfiguration.cs b/GreenshotJiraPlugin/JiraConfiguration.cs
new file mode 100644
index 000000000..89c4fcf9b
--- /dev/null
+++ b/GreenshotJiraPlugin/JiraConfiguration.cs
@@ -0,0 +1,49 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+
+namespace GreenshotJiraPlugin {
+ ///
+ /// Description of JiraConfiguration.
+ ///
+ [IniSection("Jira", Description="Greenshot Jira Plugin configuration")]
+ public class JiraConfiguration : IniSection {
+ public const string DefaultPrefix = "http://";
+ private const string DefaultUrl = DefaultPrefix + "jira";
+
+ [IniProperty("Url", Description="Base url to Jira system, without anything else", DefaultValue=DefaultUrl)]
+ public string Url { get; set; }
+
+ [IniProperty("Timeout", Description="Session timeout in minutes", DefaultValue="30")]
+ public int Timeout { get; set; }
+
+ [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
+ public OutputFormat UploadFormat { get; set; }
+
+ [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
+ public int UploadJpegQuality { get; set; }
+
+ [IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")]
+ public bool UploadReduceColors { get; set; }
+ }
+}
diff --git a/GreenshotJiraPlugin/JiraConnector.cs b/GreenshotJiraPlugin/JiraConnector.cs
new file mode 100644
index 000000000..163771019
--- /dev/null
+++ b/GreenshotJiraPlugin/JiraConnector.cs
@@ -0,0 +1,284 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using Dapplo.HttpExtensions;
+using Dapplo.HttpExtensions.Extensions;
+using Dapplo.Jira;
+using Dapplo.Jira.Converters;
+using Dapplo.Jira.Entities;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+
+namespace GreenshotJiraPlugin {
+ ///
+ /// This encapsulates the JiraClient to make it possible to change as less old Greenshot code as needed
+ ///
+ public sealed class JiraConnector : IDisposable {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraConnector));
+ private static readonly JiraConfiguration JiraConfig = IniConfig.GetIniSection();
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ // Used to remove the wsdl information from the old SOAP Uri
+ public const string DefaultPostfix = "/rpc/soap/jirasoapservice-v2?wsdl";
+ private IJiraClient _jiraClient;
+ private IssueTypeBitmapCache _issueTypeBitmapCache;
+
+ ///
+ /// Initialize some basic stuff, in the case the SVG to bitmap converter
+ ///
+ static JiraConnector()
+ {
+ CoreConfig.PropertyChanged += (sender, args) =>
+ {
+ if (args.PropertyName == nameof(CoreConfig.IconSize))
+ {
+ var jiraConnector = SimpleServiceProvider.Current.GetInstance();
+ jiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height });
+ }
+ };
+
+ }
+
+ ///
+ /// Dispose, logout the users
+ ///
+ public void Dispose() {
+ if (_jiraClient != null)
+ {
+ Logout();
+ }
+ FavIcon?.Dispose();
+ }
+
+ ///
+ /// Constructor
+ ///
+ public JiraConnector()
+ {
+ JiraConfig.Url = JiraConfig.Url.Replace(DefaultPostfix, string.Empty);
+ }
+
+ ///
+ /// Access the jira monitor
+ ///
+ public JiraMonitor Monitor { get; private set; }
+
+ public Bitmap FavIcon { get; private set; }
+
+ ///
+ /// Internal login which catches the exceptions
+ ///
+ /// true if login was done successfully
+ private async Task DoLoginAsync(string user, string password, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password))
+ {
+ return false;
+ }
+ _jiraClient = JiraClient.Create(new Uri(JiraConfig.Url));
+ _jiraClient.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height });
+ _jiraClient.SetBasicAuthentication(user, password);
+
+ _issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient);
+ try
+ {
+ Monitor = new JiraMonitor();
+ await Monitor.AddJiraInstanceAsync(_jiraClient, cancellationToken);
+
+ var favIconUri = _jiraClient.JiraBaseUri.AppendSegments("favicon.ico");
+ try
+ {
+ FavIcon = await _jiraClient.Server.GetUriContentAsync(favIconUri, cancellationToken);
+ }
+ catch (Exception ex)
+ {
+ Log.WarnFormat("Couldn't load favicon from {0}", favIconUri);
+ Log.Warn("Exception details: ", ex);
+ }
+ }
+ catch (Exception ex2)
+ {
+ Log.WarnFormat("Couldn't connect to JIRA {0}", JiraConfig.Url);
+ Log.Warn("Exception details: ", ex2);
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Use the credentials dialog, this will show if there are not correct credentials.
+ /// If there are credentials, call the real login.
+ ///
+ /// Task
+ public async Task LoginAsync(CancellationToken cancellationToken = default) {
+ Logout();
+ try {
+ // Get the system name, so the user knows where to login to
+ var credentialsDialog = new CredentialsDialog(JiraConfig.Url)
+ {
+ Name = null
+ };
+ while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK) {
+ if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password, cancellationToken)) {
+ if (credentialsDialog.SaveChecked) {
+ credentialsDialog.Confirm(true);
+ }
+ IsLoggedIn = true;
+ return;
+ }
+ // Login failed, confirm this
+ try {
+ credentialsDialog.Confirm(false);
+ } catch (ApplicationException e) {
+ // exception handling ...
+ Log.Error("Problem using the credentials dialog", e);
+ }
+ // For every windows version after XP show an incorrect password baloon
+ credentialsDialog.IncorrectPassword = true;
+ // Make sure the dialog is display, the password was false!
+ credentialsDialog.AlwaysDisplay = true;
+ }
+ } catch (ApplicationException e) {
+ // exception handling ...
+ Log.Error("Problem using the credentials dialog", e);
+ }
+
+ }
+
+ ///
+ /// End the session, if there was one
+ ///
+ public void Logout() {
+ if (_jiraClient == null || !IsLoggedIn) return;
+ Monitor.Dispose();
+ IsLoggedIn = false;
+ }
+
+ ///
+ /// check the login credentials, to prevent timeouts of the session, or makes a login
+ /// Do not use ConfigureAwait to call this, as it will move await from the UI thread.
+ ///
+ ///
+ private async Task CheckCredentialsAsync(CancellationToken cancellationToken = default) {
+ if (!IsLoggedIn) {
+ await LoginAsync(cancellationToken);
+ }
+ }
+
+ ///
+ /// Get the favorite filters
+ ///
+ /// List with filters
+ public async Task> GetFavoriteFiltersAsync(CancellationToken cancellationToken = default)
+ {
+ await CheckCredentialsAsync(cancellationToken);
+ return await _jiraClient.Filter.GetFavoritesAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get the issue for a key
+ ///
+ /// Jira issue key
+ /// CancellationToken
+ /// Issue
+ public async Task GetIssueAsync(string issueKey, CancellationToken cancellationToken = default)
+ {
+ await CheckCredentialsAsync(cancellationToken);
+ try
+ {
+ return await _jiraClient.Issue.GetAsync(issueKey, cancellationToken).ConfigureAwait(false);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// Attach the content to the jira
+ ///
+ ///
+ /// IBinaryContainer
+ ///
+ ///
+ public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default)
+ {
+ await CheckCredentialsAsync(cancellationToken);
+ using var memoryStream = new MemoryStream();
+ content.WriteToStream(memoryStream);
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ await _jiraClient.Attachment.AttachAsync(issueKey, memoryStream, content.Filename, content.ContentType, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Add a comment to the supplied issue
+ ///
+ /// Jira issue key
+ /// text
+ /// the visibility role
+ /// CancellationToken
+ public async Task AddCommentAsync(string issueKey, string body, string visibility = null, CancellationToken cancellationToken = default)
+ {
+ await CheckCredentialsAsync(cancellationToken);
+ await _jiraClient.Issue.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get the search results for the specified filter
+ ///
+ /// Filter
+ ///
+ ///
+ public async Task> SearchAsync(Filter filter, CancellationToken cancellationToken = default)
+ {
+ await CheckCredentialsAsync(cancellationToken);
+ var searchResult = await _jiraClient.Issue.SearchAsync(filter.Jql, null, new[] { "summary", "reporter", "assignee", "created", "issuetype" }, null, cancellationToken).ConfigureAwait(false);
+ return searchResult.Issues;
+ }
+
+ ///
+ /// Get the bitmap representing the issue type of an issue, from cache.
+ ///
+ /// Issue
+ /// CancellationToken
+ /// Bitmap
+ public async Task GetIssueTypeBitmapAsync(Issue issue, CancellationToken cancellationToken = default)
+ {
+ return await _issueTypeBitmapCache.GetOrCreateAsync(issue.Fields.IssueType, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get the base uri
+ ///
+ public Uri JiraBaseUri => _jiraClient.JiraBaseUri;
+
+ ///
+ /// Is the user "logged in?
+ ///
+ public bool IsLoggedIn { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotJiraPlugin/JiraDestination.cs b/GreenshotJiraPlugin/JiraDestination.cs
new file mode 100644
index 000000000..76d2314b3
--- /dev/null
+++ b/GreenshotJiraPlugin/JiraDestination.cs
@@ -0,0 +1,160 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+using Dapplo.HttpExtensions;
+using Dapplo.Jira.Entities;
+using GreenshotJiraPlugin.Forms;
+using GreenshotPlugin.Controls;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotJiraPlugin {
+ ///
+ /// Description of JiraDestination.
+ ///
+ public class JiraDestination : AbstractDestination {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraDestination));
+ private static readonly JiraConfiguration Config = IniConfig.GetIniSection();
+ private readonly Issue _jiraIssue;
+
+ public JiraDestination(Issue jiraIssue = null) {
+ _jiraIssue = jiraIssue;
+ }
+
+ public override string Designation => "Jira";
+
+ public override string Description {
+ get
+ {
+ if (_jiraIssue?.Fields?.Summary == null) {
+ return Language.GetString("jira", LangKey.upload_menu_item);
+ }
+ // Format the title of this destination
+ return _jiraIssue.Key + ": " + _jiraIssue.Fields.Summary.Substring(0, Math.Min(20, _jiraIssue.Fields.Summary.Length));
+ }
+ }
+
+ public override bool IsActive => base.IsActive && !string.IsNullOrEmpty(Config.Url);
+
+ public override bool IsDynamic => true;
+
+ public override Image DisplayIcon {
+ get
+ {
+ Image displayIcon = null;
+ var jiraConnector = SimpleServiceProvider.Current.GetInstance();
+ if (jiraConnector != null)
+ {
+ if (_jiraIssue != null)
+ {
+ // Try to get the issue type as icon
+ try
+ {
+ displayIcon = jiraConnector.GetIssueTypeBitmapAsync(_jiraIssue).Result;
+ }
+ catch (Exception ex)
+ {
+ Log.Warn($"Problem loading issue type for {_jiraIssue.Key}, ignoring", ex);
+ }
+ }
+ if (displayIcon == null)
+ {
+ displayIcon = jiraConnector.FavIcon;
+ }
+ }
+ if (displayIcon == null)
+ {
+ var resources = new ComponentResourceManager(typeof(JiraPlugin));
+ displayIcon = (Image)resources.GetObject("Jira");
+ }
+ return displayIcon;
+ }
+ }
+
+ public override IEnumerable DynamicDestinations()
+ {
+ var jiraConnector = SimpleServiceProvider.Current.GetInstance();
+ if (jiraConnector == null || !jiraConnector.IsLoggedIn) {
+ yield break;
+ }
+ foreach(var jiraDetails in jiraConnector.Monitor.RecentJiras)
+ {
+ yield return new JiraDestination(jiraDetails.JiraIssue);
+ }
+ }
+
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surfaceToUpload, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description);
+ string filename = Path.GetFileName(FilenameHelper.GetFilename(Config.UploadFormat, captureDetails));
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(Config.UploadFormat, Config.UploadJpegQuality, Config.UploadReduceColors);
+ var jiraConnector = SimpleServiceProvider.Current.GetInstance();
+ if (_jiraIssue != null) {
+ try {
+ // Run upload in the background
+ new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait),
+ async () =>
+ {
+ var surfaceContainer = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
+ await jiraConnector.AttachAsync(_jiraIssue.Key, surfaceContainer);
+ surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", _jiraIssue.Key).AbsoluteUri;
+ }
+ );
+ Log.DebugFormat("Uploaded to Jira {0}", _jiraIssue.Key);
+ exportInformation.ExportMade = true;
+ exportInformation.Uri = surfaceToUpload.UploadUrl;
+ } catch (Exception e) {
+ MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message);
+ }
+ } else {
+ var jiraForm = new JiraForm(jiraConnector);
+ jiraForm.SetFilename(filename);
+ var dialogResult = jiraForm.ShowDialog();
+ if (dialogResult == DialogResult.OK) {
+ try {
+ surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", jiraForm.GetJiraIssue().Key).AbsoluteUri;
+ // Run upload in the background
+ new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait),
+ async () =>
+ {
+ await jiraForm.UploadAsync(new SurfaceContainer(surfaceToUpload, outputSettings, filename));
+ }
+ );
+ Log.DebugFormat("Uploaded to Jira {0}", jiraForm.GetJiraIssue().Key);
+ exportInformation.ExportMade = true;
+ exportInformation.Uri = surfaceToUpload.UploadUrl;
+ } catch(Exception e) {
+ MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message);
+ }
+ }
+ }
+ ProcessExport(exportInformation, surfaceToUpload);
+ return exportInformation;
+ }
+ }
+}
diff --git a/GreenshotJiraPlugin/JiraDetails.cs b/GreenshotJiraPlugin/JiraDetails.cs
new file mode 100644
index 000000000..61f746f81
--- /dev/null
+++ b/GreenshotJiraPlugin/JiraDetails.cs
@@ -0,0 +1,71 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub: https://github.com/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using Dapplo.Jira.Entities;
+
+namespace GreenshotJiraPlugin
+{
+ public class JiraDetails : IComparable
+ {
+ public JiraDetails()
+ {
+ FirstSeenAt = SeenAt = DateTimeOffset.Now;
+ }
+
+ public string ProjectKey
+ {
+ get;
+ set;
+ }
+
+ public string Id
+ {
+ get;
+ set;
+ }
+
+ public string JiraKey => ProjectKey + "-" + Id;
+
+ public Issue JiraIssue
+ {
+ get;
+ set;
+ }
+
+ public DateTimeOffset FirstSeenAt
+ {
+ get;
+ private set;
+ }
+
+ public DateTimeOffset SeenAt
+ {
+ get;
+ set;
+ }
+
+ public int CompareTo(JiraDetails other)
+ {
+ return SeenAt.CompareTo(other.SeenAt);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/JiraEventArgs.cs b/GreenshotJiraPlugin/JiraEventArgs.cs
similarity index 63%
rename from src/Greenshot.Plugin.Jira/JiraEventArgs.cs
rename to GreenshotJiraPlugin/JiraEventArgs.cs
index d847c9392..b1c4887d6 100644
--- a/src/Greenshot.Plugin.Jira/JiraEventArgs.cs
+++ b/GreenshotJiraPlugin/JiraEventArgs.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub: https://github.com/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,17 +16,25 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
using System;
-namespace Greenshot.Plugin.Jira
+namespace GreenshotJiraPlugin
{
- public class JiraEventArgs : EventArgs
- {
- public JiraEventTypes EventType { get; set; }
+ public class JiraEventArgs : EventArgs
+ {
+ public JiraEventTypes EventType
+ {
+ get;
+ set;
+ }
- public JiraDetails Details { get; set; }
- }
-}
\ No newline at end of file
+ public JiraDetails Details
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Jira/JiraEventTypes.cs b/GreenshotJiraPlugin/JiraEventTypes.cs
similarity index 67%
rename from src/Greenshot.Plugin.Jira/JiraEventTypes.cs
rename to GreenshotJiraPlugin/JiraEventTypes.cs
index cc433c85e..36866e863 100644
--- a/src/Greenshot.Plugin.Jira/JiraEventTypes.cs
+++ b/GreenshotJiraPlugin/JiraEventTypes.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub: https://github.com/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,14 +16,14 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
-namespace Greenshot.Plugin.Jira
+namespace GreenshotJiraPlugin
{
- public enum JiraEventTypes
- {
- OrderChanged,
- DetectedNewJiraIssue
- }
+ public enum JiraEventTypes
+ {
+ OrderChanged,
+ DetectedNewJiraIssue
+ }
}
\ No newline at end of file
diff --git a/GreenshotJiraPlugin/JiraMonitor.cs b/GreenshotJiraPlugin/JiraMonitor.cs
new file mode 100644
index 000000000..4fcb083a7
--- /dev/null
+++ b/GreenshotJiraPlugin/JiraMonitor.cs
@@ -0,0 +1,218 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub: https://github.com/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using Dapplo.Jira;
+using Dapplo.Log;
+using GreenshotPlugin.Hooking;
+
+namespace GreenshotJiraPlugin
+{
+
+ ///
+ /// This class will monitor all _jira activity by registering for title changes
+ /// It keeps a list of the last "accessed" jiras, and makes it easy to upload to one.
+ /// Make sure this is instanciated on the UI thread!
+ ///
+ public class JiraMonitor : IDisposable
+ {
+ private static readonly LogSource Log = new LogSource();
+ private readonly Regex _jiraKeyPattern = new Regex(@"[A-Z][A-Z0-9]+\-[0-9]+");
+ private readonly WindowsTitleMonitor _monitor;
+ private readonly IList _jiraInstances = new List();
+ private readonly IDictionary _projectJiraClientMap = new Dictionary();
+ private readonly int _maxEntries;
+ // TODO: Add issues from issueHistory (JQL -> Where.IssueKey.InIssueHistory())
+ private IDictionary _recentJiras = new Dictionary();
+
+ ///
+ /// Register to this event to get events when new jira issues are detected
+ ///
+ public event EventHandler JiraEvent;
+
+ public JiraMonitor(int maxEntries = 40)
+ {
+ _maxEntries = maxEntries;
+ _monitor = new WindowsTitleMonitor();
+ _monitor.TitleChangeEvent += MonitorTitleChangeEvent;
+ }
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Dispose all managed resources
+ ///
+ /// when true is passed all managed resources are disposed.
+ protected void Dispose(bool disposing)
+ {
+ if (!disposing)
+ {
+ return;
+ }
+ // free managed resources
+ _monitor.TitleChangeEvent -= MonitorTitleChangeEvent;
+ _monitor.Dispose();
+ // free native resources if there are any.
+ }
+
+ ///
+ /// Retrieve the API belonging to a JiraDetails
+ ///
+ ///
+ /// IJiraClient
+ public IJiraClient GetJiraClientForKey(JiraDetails jiraDetails)
+ {
+ return _projectJiraClientMap[jiraDetails.ProjectKey];
+ }
+
+ ///
+ /// Get the "list" of recently seen Jiras
+ ///
+ public IEnumerable RecentJiras =>
+ (from jiraDetails in _recentJiras.Values
+ orderby jiraDetails.SeenAt descending
+ select jiraDetails);
+
+ ///
+ /// Check if this monitor has active instances
+ ///
+ public bool HasJiraInstances => _jiraInstances.Count > 0;
+
+ ///
+ /// Add an instance of a JIRA system
+ ///
+ /// IJiraClient
+ /// CancellationToken
+ public async Task AddJiraInstanceAsync(IJiraClient jiraInstance, CancellationToken token = default)
+ {
+ _jiraInstances.Add(jiraInstance);
+ var projects = await jiraInstance.Project.GetAllAsync(cancellationToken: token).ConfigureAwait(false);
+ if (projects != null)
+ {
+ foreach (var project in projects)
+ {
+ if (!_projectJiraClientMap.ContainsKey(project.Key))
+ {
+ _projectJiraClientMap.Add(project.Key, jiraInstance);
+ }
+ }
+ }
+ }
+
+ ///
+ /// This method will update details, like the title, and send an event to registed listeners of the JiraEvent
+ ///
+ /// Contains the jira key to retrieve the title (XYZ-1234)
+ /// Task
+ private async Task DetectedNewJiraIssueAsync(JiraDetails jiraDetails)
+ {
+ try
+ {
+ if (_projectJiraClientMap.TryGetValue(jiraDetails.ProjectKey, out var jiraClient))
+ {
+ var issue = await jiraClient.Issue.GetAsync(jiraDetails.JiraKey).ConfigureAwait(false);
+ jiraDetails.JiraIssue = issue;
+ }
+ // Send event
+ JiraEvent?.Invoke(this, new JiraEventArgs { Details = jiraDetails, EventType = JiraEventTypes.DetectedNewJiraIssue });
+ }
+ catch (Exception ex)
+ {
+ Log.Warn().WriteLine("Couldn't retrieve JIRA title: {0}", ex.Message);
+ }
+ }
+
+ ///
+ /// Handle title changes, check for JIRA
+ ///
+ ///
+ private void MonitorTitleChangeEvent(TitleChangeEventArgs eventArgs)
+ {
+ string windowTitle = eventArgs.Title;
+ if (string.IsNullOrEmpty(windowTitle))
+ {
+ return;
+ }
+ var jiraKeyMatch = _jiraKeyPattern.Match(windowTitle);
+ if (!jiraKeyMatch.Success)
+ {
+ return;
+ }
+ // Found a possible JIRA title
+ var jiraKey = jiraKeyMatch.Value;
+ var jiraKeyParts = jiraKey.Split('-');
+ var projectKey = jiraKeyParts[0];
+ var jiraId = jiraKeyParts[1];
+
+ // Check if we have a JIRA instance with a project for this key
+ if (_projectJiraClientMap.TryGetValue(projectKey, out var jiraClient))
+ {
+ // We have found a project for this _jira key, so it must be a valid & known JIRA
+ if (_recentJiras.TryGetValue(jiraKey, out var currentJiraDetails))
+ {
+ // update
+ currentJiraDetails.SeenAt = DateTimeOffset.Now;
+
+ // Notify the order change
+ JiraEvent?.Invoke(this, new JiraEventArgs { Details = currentJiraDetails, EventType = JiraEventTypes.OrderChanged });
+ // Nothing else to do
+
+ return;
+ }
+ // We detected an unknown JIRA, so add it to our list
+ currentJiraDetails = new JiraDetails
+ {
+ Id = jiraId,
+ ProjectKey = projectKey
+ };
+ _recentJiras.Add(currentJiraDetails.JiraKey, currentJiraDetails);
+
+ // Make sure we don't collect _jira's until the memory is full
+ if (_recentJiras.Count > _maxEntries)
+ {
+ // Add it to the list of recent Jiras
+ _recentJiras = (from jiraDetails in _recentJiras.Values.ToList()
+ orderby jiraDetails.SeenAt descending
+ select jiraDetails).Take(_maxEntries).ToDictionary(jd => jd.JiraKey, jd => jd);
+ }
+ // Now we can get the title from JIRA itself
+ // ReSharper disable once UnusedVariable
+ var updateTitleTask = DetectedNewJiraIssueAsync(currentJiraDetails);
+ }
+ else
+ {
+ Log.Info().WriteLine("Couldn't match possible JIRA key {0} to projects in a configured JIRA instance, ignoring", projectKey);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotJiraPlugin/JiraPlugin.cs b/GreenshotJiraPlugin/JiraPlugin.cs
new file mode 100644
index 000000000..9448829c2
--- /dev/null
+++ b/GreenshotJiraPlugin/JiraPlugin.cs
@@ -0,0 +1,138 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Windows.Forms;
+using System;
+using System.Threading.Tasks;
+using Dapplo.Log;
+using GreenshotJiraPlugin.Forms;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+using log4net;
+
+namespace GreenshotJiraPlugin {
+ ///
+ /// This is the JiraPlugin base code
+ ///
+ [Plugin("Jira", true)]
+ public class JiraPlugin : IGreenshotPlugin {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(JiraPlugin));
+ private JiraConfiguration _config;
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected void Dispose(bool disposing) {
+ if (disposing)
+ {
+ var jiraConnector = SimpleServiceProvider.Current.GetInstance();
+ jiraConnector?.Dispose();
+ }
+ }
+
+ ///
+ /// Implementation of the IGreenshotPlugin.Initialize
+ ///
+ /// true if plugin is initialized, false if not (doesn't show)
+ public bool Initialize() {
+ // Register configuration (don't need the configuration itself)
+ _config = IniConfig.GetIniSection();
+
+ // Provide the JiraConnector
+ SimpleServiceProvider.Current.AddService(new JiraConnector());
+ // Provide the IDestination
+ SimpleServiceProvider.Current.AddService(new JiraDestination());
+
+ // Make sure the log is enabled for the correct level.
+ if (Log.IsDebugEnabled)
+ {
+ LogSettings.RegisterDefaultLogger(LogLevels.Verbose);
+ }
+ else if (Log.IsInfoEnabled)
+ {
+ LogSettings.RegisterDefaultLogger(LogLevels.Info);
+ }
+ else if (Log.IsWarnEnabled)
+ {
+ LogSettings.RegisterDefaultLogger(LogLevels.Warn);
+ }
+ else if (Log.IsErrorEnabled)
+ {
+ LogSettings.RegisterDefaultLogger(LogLevels.Error);
+ }
+ else if (Log.IsErrorEnabled)
+ {
+ LogSettings.RegisterDefaultLogger(LogLevels.Error);
+ }
+ else
+ {
+ LogSettings.RegisterDefaultLogger(LogLevels.Fatal);
+ }
+
+ return true;
+ }
+
+ public void Shutdown() {
+ Log.Debug("Jira Plugin shutdown.");
+ var jiraConnector = SimpleServiceProvider.Current.GetInstance();
+ jiraConnector?.Logout();
+ }
+
+ ///
+ /// Implementation of the IPlugin.Configure
+ ///
+ public void Configure() {
+ string url = _config.Url;
+ if (ShowConfigDialog()) {
+ // check for re-login
+ var jiraConnector = SimpleServiceProvider.Current.GetInstance();
+ if (jiraConnector != null && jiraConnector.IsLoggedIn && !string.IsNullOrEmpty(url)) {
+ if (!url.Equals(_config.Url)) {
+ jiraConnector.Logout();
+ Task.Run(async () =>
+ {
+ await jiraConnector.LoginAsync();
+ });
+ }
+ }
+ }
+ }
+
+ ///
+ /// A form for username/password
+ ///
+ /// bool true if OK was pressed, false if cancel
+ private bool ShowConfigDialog()
+ {
+ var settingsForm = new SettingsForm();
+ var result = settingsForm.ShowDialog();
+ if (result == DialogResult.OK)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Jira/JiraPlugin.resx b/GreenshotJiraPlugin/JiraPlugin.resx
similarity index 100%
rename from src/Greenshot.Plugin.Jira/JiraPlugin.resx
rename to GreenshotJiraPlugin/JiraPlugin.resx
diff --git a/GreenshotJiraPlugin/LanguageKeys.cs b/GreenshotJiraPlugin/LanguageKeys.cs
new file mode 100644
index 000000000..94f162dd6
--- /dev/null
+++ b/GreenshotJiraPlugin/LanguageKeys.cs
@@ -0,0 +1,46 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotJiraPlugin {
+ public enum LangKey {
+ upload_menu_item,
+ column_assignee,
+ column_created,
+ column_issueType,
+ column_id,
+ column_reporter,
+ column_summary,
+ label_comment,
+ label_filename,
+ label_jira,
+ label_jirafilter,
+ login_error,
+ login_title,
+ settings_title,
+ label_url,
+ label_upload_format,
+ OK,
+ CANCEL,
+ upload_success,
+ upload_failure,
+ communication_wait,
+ }
+}
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-cs-CZ.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-cs-CZ.xml
similarity index 98%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-cs-CZ.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-cs-CZ.xml
index 1a7133833..0c3202c3a 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-cs-CZ.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-cs-CZ.xml
@@ -1,25 +1,25 @@
-
-
-
- Zrušit
- Pověřené osoby
- Vytvořil(a)
- ID
- Reporter ???
- Shrnutí
- Přenos dat na JIRA, prosím počkejte...
- Komentář
- Jméno souboru
- JIRA
- JIRA filtr
- Formát obrázku
- Url
- Problém při přihlášení: {0}
- Prosím, zadejte přihlašovací údaje pre Jira
- OK
- Nastavení Jira
- Došlo k chybě při nahrávání na Jira:
- Nahrát na Jira
- Obrázek úspěšně nahrán na Jira!
-
+
+
+
+ Zrušit
+ Pověřené osoby
+ Vytvořil(a)
+ ID
+ Reporter ???
+ Shrnutí
+ Přenos dat na JIRA, prosím počkejte...
+ Komentář
+ Jméno souboru
+ JIRA
+ JIRA filtr
+ Formát obrázku
+ Url
+ Problém při přihlášení: {0}
+ Prosím, zadejte přihlašovací údaje pre Jira
+ OK
+ Nastavení Jira
+ Došlo k chybě při nahrávání na Jira:
+ Nahrát na Jira
+ Obrázek úspěšně nahrán na Jira!
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-de-DE.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-de-DE.xml
similarity index 95%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-de-DE.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-de-DE.xml
index 2f6e7ea11..e32894a69 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-de-DE.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-de-DE.xml
@@ -1,65 +1,65 @@
-
-
-
-
- In Jira hochladen
-
-
- Bearbeiter
-
-
- Erstellt
-
-
- ID
-
-
- Author
-
-
- Kurzbeschreibung
-
-
- Kommentar
-
-
- Dateiname
-
-
- JIRA
-
-
- JIRA Filter
-
-
- Es gab ein Problem beim Login: {0}
-
-
- Bitte geben Sie ihre Jira Login-Daten ein
-
-
- Jira Einstellungen
-
-
- Url
-
-
- OK
-
-
- Cancel
-
-
- Das Hochladen zu Jira war erfolgreich.
-
-
- Es gab einen Fehler beim Hochladen zu Jira:
-
-
- Bildformat
-
-
- Übermittle Daten zu Jira. Bitte warten...
-
-
+
+
+
+
+ In Jira hochladen
+
+
+ Bearbeiter
+
+
+ Erstellt
+
+
+ ID
+
+
+ Author
+
+
+ Kurzbeschreibung
+
+
+ Kommentar
+
+
+ Dateiname
+
+
+ JIRA
+
+
+ JIRA Filter
+
+
+ Es gab ein Problem beim Login: {0}
+
+
+ Bitte geben Sie ihre Jira Login-Daten ein
+
+
+ Jira Einstellungen
+
+
+ Url
+
+
+ OK
+
+
+ Cancel
+
+
+ Das Hochladen zu Jira war erfolgreich.
+
+
+ Es gab einen Fehler beim Hochladen zu Jira:
+
+
+ Bildformat
+
+
+ Übermittle Daten zu Jira. Bitte warten...
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-en-US.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-en-US.xml
similarity index 92%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-en-US.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-en-US.xml
index ca64fd7a8..b64eb75f4 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-en-US.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-en-US.xml
@@ -1,65 +1,65 @@
-
-
-
-
- Upload to Jira
-
-
- Assignee
-
-
- Created
-
-
- ID
-
-
- Reporter
-
-
- Summary
-
-
- Comment
-
-
- Filename
-
-
- JIRA
-
-
- JIRA Filter
-
-
- There was a problem during the login: {0}
-
-
- Url
-
-
- Please enter your Jira login data
-
-
- Jira settings
-
-
- OK
-
-
- Cancel
-
-
- Successfully uploaded image to Jira!
-
-
- An error occurred while uploading to Jira:
-
-
- Image format
-
-
- Transferring data to JIRA, please wait...
-
-
+
+
+
+
+ Upload to Jira
+
+
+ Assignee
+
+
+ Created
+
+
+ ID
+
+
+ Reporter
+
+
+ Summary
+
+
+ Comment
+
+
+ Filename
+
+
+ JIRA
+
+
+ JIRA Filter
+
+
+ There was a problem during the login: {0}
+
+
+ Url
+
+
+ Please enter your Jira login data
+
+
+ Jira settings
+
+
+ OK
+
+
+ Cancel
+
+
+ Successfully uploaded image to Jira!
+
+
+ An error occured while uploading to Jira:
+
+
+ Image format
+
+
+ Transferring data to JIRA, please wait...
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-fr-FR.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-fr-FR.xml
similarity index 98%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-fr-FR.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-fr-FR.xml
index c27433023..9b5faa3b0 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-fr-FR.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-fr-FR.xml
@@ -1,25 +1,25 @@
-
-
-
- Annuler
- Bénéficiaire
- Créé
- ID
- Reporter
- Résumé
- Transfert de données vers JIRA, veuillez patienter...
- Commentaire
- Fichier
- JIRA
- Filtre JIRA
- Format image
- Url
- Un problème est survenu lors de l'authentification : {0}
- Veuillez saisir vos identifiants Jira
- OK
- Paramètres Jira
- Une erreur est survenue lors du téléversement vers Jira :
- Téléverser vers Jira
- L'image a été téléversée vers Jira avec succès !
-
+
+
+
+ Annuler
+ Bénéficiaire
+ Créé
+ ID
+ Reporter
+ Résumé
+ Transfert de données vers JIRA, veuillez patienter...
+ Commentaire
+ Fichier
+ JIRA
+ Filtre JIRA
+ Format image
+ Url
+ Un problème est survenu lors de l'authentification : {0}
+ Veuillez saisir vos identifiants Jira
+ OK
+ Paramètres Jira
+ Une erreur est survenue lors du téléversement vers Jira :
+ Téléverser vers Jira
+ L'image a été téléversée vers Jira avec succès !
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-id-ID.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-id-ID.xml
similarity index 98%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-id-ID.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-id-ID.xml
index 91bfd1dc5..47a72857b 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-id-ID.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-id-ID.xml
@@ -1,25 +1,25 @@
-
-
-
- Batal
- Tujuan
- Tercipta
- ID
- Pelapor
- Penjelasan
- Mentransfer data ke JIRA, tunggu sebentar...
- Komentar
- Nama berkas
- JIRA
- JIRA Filter
- Format gambar
- Url
- Masalah terjadi ketika login: {0}
- Masukkan data login Jira anda
- Oke
- Setelan Jira
- Kesalahan terjadi ketika mengunggah ke Jira:
- Unggah ke Jira
- Berhasil mengunggah gambar ke Jira!
-
+
+
+
+ Batal
+ Tujuan
+ Tercipta
+ ID
+ Pelapor
+ Penjelasan
+ Mentransfer data ke JIRA, tunggu sebentar...
+ Komentar
+ Nama berkas
+ JIRA
+ JIRA Filter
+ Format gambar
+ Url
+ Masalah terjadi ketika login: {0}
+ Masukkan data login Jira anda
+ Oke
+ Setelan Jira
+ Kesalahan terjadi ketika mengunggah ke Jira:
+ Unggah ke Jira
+ Berhasil mengunggah gambar ke Jira!
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-it-IT.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-it-IT.xml
similarity index 80%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-it-IT.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-it-IT.xml
index f69d1fb37..8b9febaf8 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-it-IT.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-it-IT.xml
@@ -1,65 +1,65 @@
-
-
-
-
- Carica su Jira
-
-
- Assegnatario
-
-
- Creata
-
-
- ID
-
-
- Segnalatore
-
-
- Riepilogo
-
-
- Commento
-
-
- Nome file
-
-
- JIRA
-
-
- Filtro JIRA
-
-
- Si è verificato un problema durante il collegamento: {0}
-
-
- URL
-
-
- Inserisci le credenziali di accesso a Jira
-
-
- Impostazioni Jira
-
-
- OK
-
-
- Annulla
-
-
- caricamento immagine su Jira completato!
-
-
- Si è verificato un errore durante il caricamento su Jira:
-
-
- Formato immagine
-
-
- Trasferimento dati verso JIRA...
-
-
+
+
+
+
+ Carica su Jira
+
+
+ Assegnatario
+
+
+ Creata
+
+
+ ID
+
+
+ Reporter
+
+
+ Riepilogo
+
+
+ Commento
+
+
+ Nome File
+
+
+ JIRA
+
+
+ Filtro JIRA
+
+
+ Si è verificato un problema durante il collegamento: {0}
+
+
+ Url
+
+
+ Inserisci le tue credenziali di collegamento a Jira
+
+
+ Impostazioni Jira
+
+
+ OK
+
+
+ Annulla
+
+
+ Immagine caricata correttamente su Jira!
+
+
+ Si è verificato un errore durante il caricamento su Jira:
+
+
+ Formato immagine
+
+
+ Trasferimento dati verso JIRA, attendere prego...
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-ja-JP.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-ja-JP.xml
similarity index 100%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-ja-JP.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-ja-JP.xml
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-kab-DZ.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-kab-DZ.xml
similarity index 100%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-kab-DZ.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-kab-DZ.xml
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-ko-KR.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-ko-KR.xml
similarity index 100%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-ko-KR.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-ko-KR.xml
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-lv-LV.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-lv-LV.xml
similarity index 100%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-lv-LV.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-lv-LV.xml
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-nl-NL.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-nl-NL.xml
similarity index 95%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-nl-NL.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-nl-NL.xml
index 05db8619e..47275ceae 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-nl-NL.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-nl-NL.xml
@@ -1,71 +1,71 @@
-
-
-
-
- Naar Jira uploaden
-
-
- Toegewezen aan
-
-
- Gecreërd
-
-
- ID
-
-
- Gemeld door
-
-
- Samenvatting
-
-
- Kommentaar
-
-
- Filename
-
-
- JIRA
-
-
- JIRA filter
-
-
- Tijdens de login is een fout opgetreden: {0}
-
-
- Url
-
-
- Gebruiker
-
-
- Password
-
-
- Geef uw Jira login data
-
-
- Jira instellingen
-
-
- OK
-
-
- Afbreken
-
-
- Het uploaden naar Jira is geslaagt!
-
-
- Tijdens het uploaden naar Jira is een fout opgetreden:
-
-
- Beeld formaat
-
-
- Gegevensoverdracht naar Jira, wachten A.U.B...
-
-
+
+
+
+
+ Naar Jira uploaden
+
+
+ Toegewezen aan
+
+
+ Gecreërd
+
+
+ ID
+
+
+ Gemeld door
+
+
+ Samenvatting
+
+
+ Kommentaar
+
+
+ Filename
+
+
+ JIRA
+
+
+ JIRA filter
+
+
+ Tijdens de login is een fout opgetreden: {0}
+
+
+ Url
+
+
+ Gebruiker
+
+
+ Password
+
+
+ Geef uw Jira login data
+
+
+ Jira instellingen
+
+
+ OK
+
+
+ Afbreken
+
+
+ Het uploaden naar Jira is geslaagt!
+
+
+ Tijdens het uploaden naar Jira is een fout opgetreden:
+
+
+ Beeld formaat
+
+
+ Gegevensoverdracht naar Jira, wachten A.U.B...
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-pl-PL.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-pl-PL.xml
similarity index 98%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-pl-PL.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-pl-PL.xml
index d7d22aca2..dc1050295 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-pl-PL.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-pl-PL.xml
@@ -1,25 +1,25 @@
-
-
-
- Anuluj
- Przypisane do
- Utworzone
- ID
- Zgłaszający
- Podsumowanie
- Trwa komunikacja z JIRA. Proszę czekać...
- Komentarz
- Nazwa pliku
- JIRA
- Filtr JIRA
- Format obrazu
- URL
- Wystąpił problem podczas logowania: {0}
- Wprowadź swoje dane logowania
- OK
- Ustawienia JIRA
- Wystąpił błąd przy wysyłaniu do JIRA:
- Wyślij do JIRA
- Wysyłanie obrazu do JIRA powiodło się!
-
+
+
+
+ Anuluj
+ Przypisane do
+ Utworzone
+ ID
+ Zgłaszający
+ Podsumowanie
+ Trwa komunikacja z JIRA. Proszę czekać...
+ Komentarz
+ Nazwa pliku
+ JIRA
+ Filtr JIRA
+ Format obrazu
+ URL
+ Wystąpił problem podczas logowania: {0}
+ Wprowadź swoje dane logowania
+ OK
+ Ustawienia JIRA
+ Wystąpił błąd przy wysyłaniu do JIRA:
+ Wyślij do JIRA
+ Wysyłanie obrazu do JIRA powiodło się!
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-pt-PT.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-pt-PT.xml
similarity index 100%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-pt-PT.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-pt-PT.xml
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-ru-RU.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-ru-RU.xml
similarity index 98%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-ru-RU.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-ru-RU.xml
index 8e3ad5003..b983711a1 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-ru-RU.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-ru-RU.xml
@@ -1,25 +1,25 @@
-
-
-
- Отмена
- Представитель
- Создан
- ID
- Корреспондент
- Резюме
- Передача данных в JIRA, подождите, пожалуйста!...
- Комментарий
- Имя файла
- JIRA
- Фильтр JIRA
- Формат изображения
- Url
- Произошла ошибка во время входа: {0}
- Пожалуйста, введите ваши регистрационные данные Jira
- OK
- JIRA параметры
- Произошла ошибка при загрузке на Jira:
- Загрузить на Jira
- Изображение успешно загружено на Jira!
-
+
+
+
+ Отмена
+ Представитель
+ Создан
+ ID
+ Корреспондент
+ Резюме
+ Передача данных в JIRA, подождите, пожалуйста!...
+ Комментарий
+ Имя файла
+ JIRA
+ Фильтр JIRA
+ Формат изображения
+ Url
+ Произошла ошибка во время входа: {0}
+ Пожалуйста, введите ваши регистрационные данные Jira
+ OK
+ JIRA параметры
+ Произошла ошибка при загрузке на Jira:
+ Загрузить на Jira
+ Изображение успешно загружено на Jira!
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-sr-RS.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-sr-RS.xml
similarity index 98%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-sr-RS.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-sr-RS.xml
index 6e1d3e2f2..bc35c25cc 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-sr-RS.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-sr-RS.xml
@@ -1,25 +1,25 @@
-
-
-
- Откажи
- Представник
- Направљено
- ИД
- Кореспондент
- Резиме
- Преносим податке на Џиру…
- Коментар
- Назив датотеке
- Џира
- Филтер Џире
- Формат слике:
- Адреса:
- Дошло је до грешке при пријављивању: {0}
- Унесите податке за пријављивање на Џиру
- У реду
- Поставке Џире
- Дошло је до грешке при отпремању на Џиру:
- Отпреми на Џиру
- Слика је успешно постављена на Џиру.
-
+
+
+
+ Откажи
+ Представник
+ Направљено
+ ИД
+ Кореспондент
+ Резиме
+ Преносим податке на Џиру…
+ Коментар
+ Назив датотеке
+ Џира
+ Филтер Џире
+ Формат слике:
+ Адреса:
+ Дошло је до грешке при пријављивању: {0}
+ Унесите податке за пријављивање на Џиру
+ У реду
+ Поставке Џире
+ Дошло је до грешке при отпремању на Џиру:
+ Отпреми на Џиру
+ Слика је успешно постављена на Џиру.
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-sv-SE.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-sv-SE.xml
similarity index 100%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-sv-SE.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-sv-SE.xml
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-uk-UA.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-uk-UA.xml
similarity index 98%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-uk-UA.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-uk-UA.xml
index d9763c9c4..59a9bcc8f 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-uk-UA.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-uk-UA.xml
@@ -1,25 +1,25 @@
-
-
-
- Вивантажити на Jira
- Представник
- Створено
- Ідентифікатор
- Кореспондент
- Зведення
- Коментар
- Назва файла
- JIRA
- Фільтр JIRA
- Відбулась помилка під час авторизації: {0}
- Посилання
- Будь ласка, введіть дані облікового запису Jira
- Параметри Jira
- Гаразд
- Скасувати
- Зображення вдало вивантажено на Jira!
- Відбулась помилка під час вивантаження на Jira:
- Формат зображення
- Передача даних на JIRA, будь ласка, зачекайте...
-
-
+
+
+
+ Вивантажити на Jira
+ Представник
+ Створено
+ Ідентифікатор
+ Кореспондент
+ Зведення
+ Коментар
+ Назва файла
+ JIRA
+ Фільтр JIRA
+ Відбулась помилка під час авторизації: {0}
+ Посилання
+ Будь ласка, введіть дані облікового запису Jira
+ Параметри Jira
+ Гаразд
+ Скасувати
+ Зображення вдало вивантажено на Jira!
+ Відбулась помилка під час вивантаження на Jira:
+ Формат зображення
+ Передача даних на JIRA, будь ласка, зачекайте...
+
+
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-zh-CN.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-zh-CN.xml
similarity index 95%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-zh-CN.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-zh-CN.xml
index e69deefc2..779eb0c0c 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-zh-CN.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-zh-CN.xml
@@ -1,65 +1,65 @@
-
-
-
-
- 上传到 Jira
-
-
- 委托人
-
-
- 已创建
-
-
- ID
-
-
- 报告人
-
-
- 摘要
-
-
- 注释
-
-
- 文件名
-
-
- JIRA
-
-
- JIRA 过滤
-
-
- 登录过程中出现问题: {0}
-
-
- Url
-
-
- 请输入你的 Jira 登录数据
-
-
- Jira 设置
-
-
- 确定
-
-
- 取消
-
-
- 已成功上传图片到 Jira !
-
-
- 上传到 Jira 时出现错误:
-
-
- 图片格式
-
-
- 正在传输数据到 JIRA ,请稍候...
-
-
+
+
+
+
+ 上传到 Jira
+
+
+ 委托人
+
+
+ 已创建
+
+
+ ID
+
+
+ 报告人
+
+
+ 摘要
+
+
+ 注释
+
+
+ 文件名
+
+
+ JIRA
+
+
+ JIRA 过滤
+
+
+ 登录过程中出现问题: {0}
+
+
+ Url
+
+
+ 请输入你的 Jira 登录数据
+
+
+ Jira 设置
+
+
+ 确定
+
+
+ 取消
+
+
+ 已成功上传图片到 Jira !
+
+
+ 上传到 Jira 时出现错误:
+
+
+ 图片格式
+
+
+ 正在传输数据到 JIRA ,请稍候...
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Jira/Languages/language_jira-zh-TW.xml b/GreenshotJiraPlugin/Languages/language_jiraplugin-zh-TW.xml
similarity index 98%
rename from src/Greenshot.Plugin.Jira/Languages/language_jira-zh-TW.xml
rename to GreenshotJiraPlugin/Languages/language_jiraplugin-zh-TW.xml
index f3e51c75b..b2ea00222 100644
--- a/src/Greenshot.Plugin.Jira/Languages/language_jira-zh-TW.xml
+++ b/GreenshotJiraPlugin/Languages/language_jiraplugin-zh-TW.xml
@@ -1,25 +1,25 @@
-
-
-
- 取消
- 指派
- 建立日期
- ID
- 報導
- 摘要
- 正在傳輸資料到 JIRA,請稍候...
- 註解
- 檔案名稱
- JIRA
- JIRA 篩選
- 圖片格式
- URL
- 登入期間發生問題: {0}
- 請輸入您的 JIRA 登入資料
- 確定
- JIRA 設定
- 上傳到 JIRA 時發生錯誤:
- 上傳到
- 上傳圖片到 JIRA 成功!
-
+
+
+
+ 取消
+ 指派
+ 建立日期
+ ID
+ 報導
+ 摘要
+ 正在傳輸資料到 JIRA,請稍候...
+ 註解
+ 檔案名稱
+ JIRA
+ JIRA 篩選
+ 圖片格式
+ URL
+ 登入期間發生問題: {0}
+ 請輸入您的 JIRA 登入資料
+ 確定
+ JIRA 設定
+ 上傳到 JIRA 時發生錯誤:
+ 上傳到
+ 上傳圖片到 JIRA 成功!
+
\ No newline at end of file
diff --git a/GreenshotJiraPlugin/Log4NetLogger.cs b/GreenshotJiraPlugin/Log4NetLogger.cs
new file mode 100644
index 000000000..186e7e75b
--- /dev/null
+++ b/GreenshotJiraPlugin/Log4NetLogger.cs
@@ -0,0 +1,120 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using Dapplo.Log;
+using log4net;
+
+namespace GreenshotJiraPlugin
+{
+ ///
+ /// Used to make Dapplo.Log, used in Dapplo.Jira, write to Log4net
+ ///
+ public class Log4NetLogger : AbstractLogger
+ {
+ private ILog GetLogger(LogSource logSource)
+ {
+ return logSource.SourceType != null ? LogManager.GetLogger(logSource.SourceType) : LogManager.GetLogger(logSource.Source);
+ }
+
+ ///
+ /// Write the supplied information to a log4net.ILog
+ ///
+ /// LogInfo
+ /// string
+ /// params object[]
+ public override void Write(LogInfo logInfo, string messageTemplate, params object[] propertyValues)
+ {
+ var log = GetLogger(logInfo.Source);
+
+ switch (logInfo.LogLevel)
+ {
+ case LogLevels.Verbose:
+ case LogLevels.Debug:
+ if (propertyValues != null)
+ log.DebugFormat(messageTemplate, propertyValues);
+ else
+ log.Debug(messageTemplate);
+ break;
+ case LogLevels.Error:
+ if (propertyValues != null)
+ log.ErrorFormat(messageTemplate, propertyValues);
+ else
+ log.Error(messageTemplate);
+ break;
+ case LogLevels.Fatal:
+ if (propertyValues != null)
+ log.FatalFormat(messageTemplate, propertyValues);
+ else
+ log.Fatal(messageTemplate);
+ break;
+ case LogLevels.Info:
+ if (propertyValues != null)
+ log.InfoFormat(messageTemplate, propertyValues);
+ else
+ log.Info(messageTemplate);
+ break;
+ case LogLevels.Warn:
+ if (propertyValues != null)
+ log.WarnFormat(messageTemplate, propertyValues);
+ else
+ log.Warn(messageTemplate);
+ break;
+ }
+ }
+
+ ///
+ /// Make sure there are no newlines passed
+ ///
+ ///
+ ///
+ ///
+ public override void WriteLine(LogInfo logInfo, string messageTemplate, params object[] logParameters)
+ {
+ Write(logInfo, messageTemplate, logParameters);
+ }
+
+ ///
+ /// Test if a certain LogLevels enum is enabled
+ ///
+ /// LogLevels value
+ /// LogSource to check for
+ /// bool true if the LogLevels enum is enabled
+ public override bool IsLogLevelEnabled(LogLevels level, LogSource logSource = null)
+ {
+ var log = GetLogger(logSource);
+ switch (level)
+ {
+ case LogLevels.Verbose:
+ case LogLevels.Debug:
+ return log.IsDebugEnabled;
+ case LogLevels.Error:
+ return log.IsErrorEnabled;
+ case LogLevels.Fatal:
+ return log.IsFatalEnabled;
+ case LogLevels.Info:
+ return log.IsInfoEnabled;
+ case LogLevels.Warn:
+ return log.IsWarnEnabled;
+ }
+ return false;
+ }
+ }
+}
diff --git a/GreenshotOCRCommand/COMWrapper.cs b/GreenshotOCRCommand/COMWrapper.cs
new file mode 100644
index 000000000..e64824f8d
--- /dev/null
+++ b/GreenshotOCRCommand/COMWrapper.cs
@@ -0,0 +1,512 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.Remoting;
+using System.Runtime.Remoting.Messaging;
+using System.Runtime.Remoting.Proxies;
+
+namespace Greenshot.Interop {
+ ///
+ /// Wraps a late-bound COM server.
+ ///
+ public sealed class COMWrapper : RealProxy, IDisposable, IRemotingTypeInfo {
+ private const int MK_E_UNAVAILABLE = -2147221021;
+ private const int CO_E_CLASSSTRING = -2147221005;
+
+ ///
+ /// Holds reference to the actual COM object which is wrapped by this proxy
+ ///
+ private readonly object _comObject;
+
+ ///
+ /// Type of the COM object, set on constructor after getting the COM reference
+ ///
+ private readonly Type _comType;
+
+ ///
+ /// The type of which method calls are intercepted and executed on the COM object.
+ ///
+ private readonly Type _interceptType;
+
+ ///
+ /// Gets a COM object and returns the transparent proxy which intercepts all calls to the object
+ ///
+ /// Interface which defines the method and properties to intercept
+ /// Transparent proxy to the real proxy for the object
+ /// T must be an interface decorated with the attribute.
+ public static T GetInstance() {
+ Type type = typeof(T);
+ if (null == type) {
+ throw new ArgumentNullException(nameof(T));
+ }
+ if (!type.IsInterface) {
+ throw new ArgumentException("The specified type must be an interface.", nameof(T));
+ }
+
+ ComProgIdAttribute progIdAttribute = ComProgIdAttribute.GetAttribute(type);
+ if (string.IsNullOrEmpty(progIdAttribute?.Value)) {
+ throw new ArgumentException("The specified type must define a ComProgId attribute.", nameof(T));
+ }
+ string progId = progIdAttribute.Value;
+
+ object comObject = null;
+ try {
+ comObject = Marshal.GetActiveObject(progId);
+ } catch (COMException comE) {
+ if (comE.ErrorCode == MK_E_UNAVAILABLE)
+ {
+ Debug.WriteLine($"No current instance of {progId} object available.");
+ }
+ else if (comE.ErrorCode == CO_E_CLASSSTRING)
+ {
+ Debug.WriteLine($"Unknown progId {progId}");
+ }
+ } catch (Exception ex) {
+ Debug.WriteLine($"Error getting active object for {progId} {ex.Message}");
+ }
+
+ if (comObject != null) {
+ COMWrapper wrapper = new COMWrapper(comObject, type);
+ return (T)wrapper.GetTransparentProxy();
+ }
+ return default;
+ }
+
+ ///
+ /// Gets or creates a COM object and returns the transparent proxy which intercepts all calls to the object
+ /// The ComProgId can be a normal ComProgId or a GUID prefixed with "clsid:"
+ ///
+ /// Interface which defines the method and properties to intercept
+ /// Transparent proxy to the real proxy for the object
+ /// The type must be an interface decorated with the attribute.
+ public static T GetOrCreateInstance() {
+ Type type = typeof(T);
+ if (null == type) {
+ throw new ArgumentNullException(nameof(T));
+ }
+ if (!type.IsInterface) {
+ throw new ArgumentException("The specified type must be an interface.", nameof(T));
+ }
+
+ ComProgIdAttribute progIdAttribute = ComProgIdAttribute.GetAttribute(type);
+ if (string.IsNullOrEmpty(progIdAttribute?.Value)) {
+ throw new ArgumentException("The specified type must define a ComProgId attribute.", nameof(T));
+ }
+
+ object comObject = null;
+ Type comType = null;
+ string progId = progIdAttribute.Value;
+
+ try {
+ comObject = Marshal.GetActiveObject(progId);
+ } catch (COMException comE) {
+ if (comE.ErrorCode == MK_E_UNAVAILABLE)
+ {
+ Debug.WriteLine($"No current instance of {progId} object available.");
+ }
+ else if (comE.ErrorCode == CO_E_CLASSSTRING)
+ {
+ Debug.WriteLine($"Unknown progId {progId}");
+ }
+ } catch (Exception ex) {
+ Debug.WriteLine($"Error getting active object for {progId} {ex.Message}");
+ }
+ // Did we get the current instance? If not, try to create a new
+ if (comObject == null) {
+ try {
+ comType = Type.GetTypeFromProgID(progId, true);
+ } catch (Exception) {
+ Debug.WriteLine($"Error getting type for {progId}");
+ }
+ if (comType != null) {
+ try {
+ comObject = Activator.CreateInstance(comType);
+ if (comObject != null) {
+ Debug.WriteLine($"Created new instance of {progId} object.");
+ }
+ } catch (Exception ex) {
+ Debug.WriteLine($"Error creating object for {progId} {ex.Message}");
+ }
+ }
+ }
+ if (comObject != null) {
+ COMWrapper wrapper = new COMWrapper(comObject, type);
+ return (T)wrapper.GetTransparentProxy();
+ }
+ return default;
+ }
+
+ ///
+ /// Wrap an object and return the transparent proxy which intercepts all calls to the object
+ ///
+ /// An object to intercept
+ /// Interface which defines the method and properties to intercept
+ /// Transparent proxy to the real proxy for the object
+ private static object Wrap(object comObject, Type type) {
+ if (null == comObject) {
+ throw new ArgumentNullException(nameof(comObject));
+ }
+ if (null == type) {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ COMWrapper wrapper = new COMWrapper(comObject, type);
+ return wrapper.GetTransparentProxy();
+ }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ /// The COM object to wrap.
+ ///
+ ///
+ /// The interface type to impersonate.
+ ///
+ private COMWrapper(object comObject, Type type)
+ : base(type) {
+ _comObject = comObject;
+ _comType = comObject.GetType();
+ _interceptType = type;
+ }
+
+ ///
+ /// If is not called, we need to make
+ /// sure that the COM object is still cleaned up.
+ ///
+ ~COMWrapper() {
+ Dispose(false);
+ }
+
+ ///
+ /// Cleans up the COM object.
+ ///
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Release the COM reference
+ ///
+ ///
+ /// if this was called from the
+ /// interface.
+ ///
+ private void Dispose(bool disposing) {
+ if (disposing && null != _comObject) {
+ if (Marshal.IsComObject(_comObject)) {
+ try {
+ while (Marshal.ReleaseComObject(_comObject) > 0)
+ {
+ }
+ } catch (Exception ex) {
+ Debug.WriteLine($"Problem releasing {_comType}");
+ Debug.WriteLine("Error: " + ex);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Returns a string representing the wrapped object.
+ ///
+ ///
+ /// The full name of the intercepted type.
+ ///
+ public override string ToString() {
+ return _interceptType.FullName;
+ }
+
+ ///
+ /// Returns the hash code of the wrapped object.
+ ///
+ ///
+ /// The hash code of the wrapped object.
+ ///
+ public override int GetHashCode() {
+ return _comObject.GetHashCode();
+ }
+
+ ///
+ /// Compares this object to another.
+ ///
+ ///
+ /// The value to compare to.
+ ///
+ ///
+ /// if the objects are equal.
+ ///
+ public override bool Equals(object value) {
+ if (null != value && RemotingServices.IsTransparentProxy(value)) {
+ if (RemotingServices.GetRealProxy(value) is COMWrapper wrapper) {
+ return _comObject == wrapper._comObject;
+ }
+ }
+
+ return base.Equals(value);
+ }
+
+ ///
+ /// Returns the base type for a reference type.
+ ///
+ ///
+ /// The reference type.
+ ///
+ ///
+ /// The base value type.
+ ///
+ ///
+ /// is .
+ ///
+ private static Type GetByValType(Type byRefType) {
+ if (null == byRefType) {
+ throw new ArgumentNullException(nameof(byRefType));
+ }
+
+ if (byRefType.IsByRef) {
+ string name = byRefType.FullName;
+ name = name.Substring(0, name.Length - 1);
+ byRefType = byRefType.Assembly.GetType(name, true);
+ }
+
+ return byRefType;
+ }
+
+ ///
+ /// Intercept method calls
+ ///
+ ///
+ /// Contains information about the method being called
+ ///
+ ///
+ /// A .
+ ///
+ public override IMessage Invoke(IMessage myMessage) {
+ IMethodCallMessage callMessage = myMessage as IMethodCallMessage;
+
+ MethodInfo method = callMessage?.MethodBase as MethodInfo;
+ if (method == null)
+ {
+ if (callMessage != null)
+ {
+ Debug.WriteLine($"Unrecognized Invoke call: {callMessage.MethodBase}");
+ }
+ return null;
+ }
+
+ object returnValue = null;
+ object[] outArgs = null;
+ int outArgsCount = 0;
+
+ string methodName = method.Name;
+ Type returnType = method.ReturnType;
+ BindingFlags flags = BindingFlags.InvokeMethod;
+ int argCount = callMessage.ArgCount;
+
+ ParameterModifier[] argModifiers = null;
+ ParameterInfo[] parameters = null;
+
+ if ("Dispose" == methodName && 0 == argCount && typeof(void) == returnType) {
+ Dispose();
+ } else if ("ToString" == methodName && 0 == argCount && typeof(string) == returnType) {
+ returnValue = ToString();
+ } else if ("GetType" == methodName && 0 == argCount && typeof(Type) == returnType) {
+ returnValue = _interceptType;
+ } else if ("GetHashCode" == methodName && 0 == argCount && typeof(int) == returnType) {
+ returnValue = GetHashCode();
+ } else if ("Equals" == methodName && 1 == argCount && typeof(bool) == returnType) {
+ returnValue = Equals(callMessage.Args[0]);
+ } else if (1 == argCount && typeof(void) == returnType && (methodName.StartsWith("add_") || methodName.StartsWith("remove_"))) {
+ if (!(callMessage.InArgs[0] is Delegate handler)) {
+ return new ReturnMessage(new ArgumentNullException(nameof(handler)), callMessage);
+ }
+ } else {
+ var invokeObject = _comObject;
+ var invokeType = _comType;
+
+ ParameterInfo parameter;
+ object[] args;
+ if (methodName.StartsWith("get_")) {
+ // Property Get
+ methodName = methodName.Substring(4);
+ flags = BindingFlags.GetProperty;
+ args = callMessage.InArgs;
+ } else if (methodName.StartsWith("set_")) {
+ // Property Set
+ methodName = methodName.Substring(4);
+ flags = BindingFlags.SetProperty;
+ args = callMessage.InArgs;
+ } else {
+ args = callMessage.Args;
+ if (null != args && 0 != args.Length) {
+ // Modifiers for ref / out parameters
+ argModifiers = new ParameterModifier[1];
+ argModifiers[0] = new ParameterModifier(args.Length);
+
+ parameters = method.GetParameters();
+ for (int i = 0; i < parameters.Length; i++) {
+ parameter = parameters[i];
+ if (parameter.IsOut || parameter.ParameterType.IsByRef) {
+ argModifiers[0][i] = true;
+ outArgsCount++;
+ }
+ }
+
+ if (0 == outArgsCount) {
+ argModifiers = null;
+ }
+ }
+ }
+
+ // Un-wrap wrapped COM objects before passing to the method
+ COMWrapper[] originalArgs;
+ COMWrapper wrapper;
+ Type byValType;
+ if (null == args || 0 == args.Length) {
+ originalArgs = null;
+ } else {
+ originalArgs = new COMWrapper[args.Length];
+ for (int i = 0; i < args.Length; i++) {
+ if (null != args[i] && RemotingServices.IsTransparentProxy(args[i])) {
+ wrapper = RemotingServices.GetRealProxy(args[i]) as COMWrapper;
+ if (null != wrapper) {
+ originalArgs[i] = wrapper;
+ args[i] = wrapper._comObject;
+ }
+ } else if (argModifiers != null && (0 != outArgsCount && argModifiers[0][i])) {
+ byValType = GetByValType(parameters[i].ParameterType);
+ if (byValType.IsInterface) {
+ // If we're passing a COM object by reference, and
+ // the parameter is null, we need to pass a
+ // DispatchWrapper to avoid a type mismatch exception.
+ if (null == args[i]) {
+ args[i] = new DispatchWrapper(null);
+ }
+ } else if (typeof(decimal) == byValType) {
+ // If we're passing a decimal value by reference,
+ // we need to pass a CurrencyWrapper to avoid a
+ // type mismatch exception.
+ // http://support.microsoft.com/?kbid=837378
+ args[i] = new CurrencyWrapper(args[i]);
+ }
+ }
+ }
+ }
+
+ try {
+ returnValue = invokeType.InvokeMember(methodName, flags, null, invokeObject, args, argModifiers, null, null);
+ } catch (Exception ex) {
+ return new ReturnMessage(ex, callMessage);
+ }
+
+ // Handle enum and interface return types
+ if (null != returnValue) {
+ if (returnType.IsInterface) {
+ // Wrap the returned value in an intercepting COM wrapper
+ if (Marshal.IsComObject(returnValue)) {
+ returnValue = Wrap(returnValue, returnType);
+ }
+ } else if (returnType.IsEnum) {
+ // Convert to proper Enum type
+ returnValue = Enum.Parse(returnType, returnValue.ToString());
+ }
+ }
+
+ // Handle out args
+ if (0 != outArgsCount) {
+ if (args != null && parameters != null)
+ {
+ outArgs = new object[args.Length];
+ for (int i = 0; i < parameters.Length; i++) {
+ if (argModifiers != null && !argModifiers[0][i]) {
+ continue;
+ }
+
+ var arg = args[i];
+ if (null == arg) {
+ continue;
+ }
+
+ parameter = parameters[i];
+ wrapper = null;
+
+ byValType = GetByValType(parameter.ParameterType);
+ if (typeof(decimal) == byValType) {
+ if (arg is CurrencyWrapper) {
+ arg = ((CurrencyWrapper)arg).WrappedObject;
+ }
+ } else if (byValType.IsEnum) {
+ arg = Enum.Parse(byValType, arg.ToString());
+ } else if (byValType.IsInterface) {
+ if (Marshal.IsComObject(arg)) {
+ if (originalArgs != null)
+ {
+ wrapper = originalArgs[i];
+ }
+ if (null != wrapper && wrapper._comObject != arg) {
+ wrapper.Dispose();
+ wrapper = null;
+ }
+
+ if (null == wrapper) {
+ wrapper = new COMWrapper(arg, byValType);
+ }
+ arg = wrapper.GetTransparentProxy();
+ }
+ }
+ outArgs[i] = arg;
+ }
+ }
+ }
+ }
+
+ return new ReturnMessage(returnValue, outArgs, outArgsCount, callMessage.LogicalCallContext, callMessage);
+ }
+
+ ///
+ /// Implementation for the interface IRemotingTypeInfo
+ /// This makes it possible to cast the COMWrapper
+ ///
+ /// Type to cast to
+ /// object to cast
+ ///
+ public bool CanCastTo(Type toType, object o) {
+ bool returnValue = _interceptType.IsAssignableFrom(toType);
+ return returnValue;
+ }
+
+ ///
+ /// Implementation for the interface IRemotingTypeInfo
+ ///
+ public string TypeName {
+ get {
+ throw new NotSupportedException();
+ }
+ set {
+ throw new NotSupportedException();
+ }
+ }
+ }
+}
diff --git a/GreenshotOCRCommand/ComProgIdAttribute.cs b/GreenshotOCRCommand/ComProgIdAttribute.cs
new file mode 100644
index 000000000..ae581d031
--- /dev/null
+++ b/GreenshotOCRCommand/ComProgIdAttribute.cs
@@ -0,0 +1,78 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+
+namespace Greenshot.Interop {
+ ///
+ /// An attribute to specifiy the ProgID of the COM class to create. (As suggested by Kristen Wegner)
+ ///
+ [AttributeUsage(AttributeTargets.Interface)]
+ public sealed class ComProgIdAttribute : Attribute {
+ ///
+ /// Extracts the attribute from the specified type.
+ ///
+ ///
+ /// The interface type.
+ ///
+ ///
+ /// The .
+ ///
+ ///
+ /// is .
+ ///
+ public static ComProgIdAttribute GetAttribute(Type interfaceType) {
+ if (null == interfaceType) {
+ throw new ArgumentNullException(nameof(interfaceType));
+ }
+
+ Type attributeType = typeof(ComProgIdAttribute);
+ object[] attributes = interfaceType.GetCustomAttributes(attributeType, false);
+
+ if (0 == attributes.Length) {
+ Type[] interfaces = interfaceType.GetInterfaces();
+ for (int i = 0; i < interfaces.Length; i++) {
+ interfaceType = interfaces[i];
+ attributes = interfaceType.GetCustomAttributes(attributeType, false);
+ if (0 != attributes.Length) {
+ break;
+ }
+ }
+ }
+
+ if (0 == attributes.Length) {
+ return null;
+ }
+ return (ComProgIdAttribute)attributes[0];
+ }
+
+ /// Constructor
+ /// The COM ProgID.
+ public ComProgIdAttribute(string value) {
+ Value = value;
+ }
+
+ ///
+ /// Returns the COM ProgID
+ ///
+ public string Value { get; }
+ }
+}
diff --git a/GreenshotOCRCommand/GreenshotOCRCommand.csproj b/GreenshotOCRCommand/GreenshotOCRCommand.csproj
new file mode 100644
index 000000000..4267325f2
--- /dev/null
+++ b/GreenshotOCRCommand/GreenshotOCRCommand.csproj
@@ -0,0 +1,14 @@
+
+
+
+ GreenshotOCRCommand
+ GreenshotOCRCommand
+ WinExe
+
+
+
+
+
+
+
+
diff --git a/src/Greenshot.Plugin.Jira/Forms/JiraFormBase.cs b/GreenshotOCRCommand/Modi/CompressionLevel.cs
similarity index 66%
rename from src/Greenshot.Plugin.Jira/Forms/JiraFormBase.cs
rename to GreenshotOCRCommand/Modi/CompressionLevel.cs
index 10ea7556c..87a3a2e7c 100644
--- a/src/Greenshot.Plugin.Jira/Forms/JiraFormBase.cs
+++ b/GreenshotOCRCommand/Modi/CompressionLevel.cs
@@ -1,29 +1,29 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: https://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using Greenshot.Base.Controls;
-
-namespace Greenshot.Plugin.Jira.Forms
-{
- public class JiraFormBase : GreenshotForm
- {
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotOCRCommand.Modi
+{
+ public enum CompressionLevel {
+ miCOMP_LEVEL_LOW = 0,
+ miCOMP_LEVEL_MEDIUM = 1,
+ miCOMP_LEVEL_HIGH = 2
+ }
}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Modi/FileFormat.cs b/GreenshotOCRCommand/Modi/FileFormat.cs
new file mode 100644
index 000000000..bd738cdaf
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/FileFormat.cs
@@ -0,0 +1,30 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotOCR
+{
+ public enum FileFormat {
+ miFILE_FORMAT_DEFAULTVALUE = -1,
+ miFILE_FORMAT_TIFF = 1,
+ miFILE_FORMAT_TIFF_LOSSLESS = 2,
+ miFILE_FORMAT_MDI = 4
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Modi/ICommon.cs b/GreenshotOCRCommand/Modi/ICommon.cs
new file mode 100644
index 000000000..a8995c826
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/ICommon.cs
@@ -0,0 +1,32 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+
+namespace GreenshotOCRCommand.Modi
+{
+ ///
+ /// Base class for the common properties of the Modi interfaces
+ ///
+ public interface ICommon : IDisposable {
+ IDocument Application { get; }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Modi/IDispatch.cs b/GreenshotOCRCommand/Modi/IDispatch.cs
new file mode 100644
index 000000000..f2d5e9ed0
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/IDispatch.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.CustomMarshalers;
+
+namespace GreenshotOCRCommand.Modi {
+ [ComImport, Guid("00020400-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IDispatch {
+ void Reserved();
+ [PreserveSig]
+ int GetTypeInfo(uint nInfo, int lcid, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))] out Type typeInfo);
+ }
+}
diff --git a/GreenshotOCRCommand/Modi/IDocument.cs b/GreenshotOCRCommand/Modi/IDocument.cs
new file mode 100644
index 000000000..6b1d44e99
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/IDocument.cs
@@ -0,0 +1,82 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using Greenshot.Interop;
+using GreenshotOCR;
+
+namespace GreenshotOCRCommand.Modi {
+ ///
+ /// The MODI Document object represents an ordered collection of document images saved as a single file.
+ /// You can use the Create method to load an existing MDI or TIF file, or to create an empty document that you can populate with images from other documents.
+ /// The OCR method performs OCR on all pages in the document, and the OnOCRProgress event reports the status of the operation and allows the user to cancel it.
+ /// The Dirty property lets you know whether your document has unsaved OCR results or changes.
+ /// The SaveAs method allows you to specify an image file format and a compression level.
+ /// You can also use the PrintOut method to print the document to a printer or a file.
+ ///
+ [ComProgId("MODI.Document")]
+ public interface IDocument : ICommon {
+ ///
+ /// Closes the document.
+ ///
+ ///
+ void Close(bool saveCall);
+
+ ///
+ /// The document's collection of pages.
+ ///
+ IImages Images
+ {
+ get;
+ }
+
+ ///
+ /// Occurs periodically during an optical character recognition (OCR) operation. Returns the estimated percentage of the OCR operation that is complete, and allows the user to cancel the operation.
+ ///
+ // event OnOCRProgress { get; }
+
+ ///
+ /// Indicates whether the active document has unsaved changes.
+ ///
+ bool Dirty { get; }
+
+ ///
+ /// Creates a new document.
+ ///
+ /// Optional String. The path and filename of the optional document file that is to be loaded into the new document.
+ void Create(string file);
+
+ ///
+ /// Performs optical character recognition (OCR) on the specified document or image.
+ ///
+ /// ModiLanguage
+ /// Optional Boolean. Specifies whether the OCR engine attempts to determine the orientation of the page. Default is true.
+ /// Optional Boolean. Specifies whether the OCR engine attempts to "de-skew" the page to correct for small angles of misalignment from the vertical. Default is true.
+ void OCR(ModiLanguage language, bool orientimage, bool straightenImage);
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ void SaveAs(string filename, FileFormat fileFormat, CompressionLevel compressionLevel);
+ }
+}
diff --git a/GreenshotOCRCommand/Modi/IImage.cs b/GreenshotOCRCommand/Modi/IImage.cs
new file mode 100644
index 000000000..f53ffa089
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/IImage.cs
@@ -0,0 +1,41 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotOCRCommand.Modi
+{
+ ///
+ /// Describes the page in a scan
+ ///
+ public interface IImage : ICommon {
+ ILayout Layout {
+ get;
+ }
+
+ long BitsPerPixel { get; }
+ CompressionLevel Compression { get; }
+ //IPictureDisp Picture { get; }
+ int PixelHeight { get; }
+ int PixelWidth { get; }
+ //IPictureDisp Thumbnail { get; }
+ int XDPI { get; }
+ int YDPI { get; }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Modi/IImages.cs b/GreenshotOCRCommand/Modi/IImages.cs
new file mode 100644
index 000000000..7eeeb6401
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/IImages.cs
@@ -0,0 +1,41 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Collections;
+
+namespace GreenshotOCRCommand.Modi
+{
+ ///
+ /// Use the Images accessor property of the Document object to return an Images collection.
+ /// Use the Item property of the Images collection to return an Image object and gain access to its OCR Layout
+ /// (including recognized Text and Words), the properties that describe its dimensions and format (BitsPerPixel, Compression, PixelHeight, PixelWidth, XDPI, and YDPI),
+ /// and its Picture and Thumbnail images.
+ ///
+ public interface IImages : ICommon, IEnumerable {
+ int Count {
+ get;
+ }
+ IImage this [int index] {
+ get;
+ }
+ new IEnumerator GetEnumerator();
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Modi/ILayout.cs b/GreenshotOCRCommand/Modi/ILayout.cs
new file mode 100644
index 000000000..2b8d8beb5
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/ILayout.cs
@@ -0,0 +1,55 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotOCRCommand.Modi
+{
+ ///
+ /// Layout of the IImage
+ ///
+ public interface ILayout : ICommon {
+ ///
+ /// Returns the recognized text as a Unicode string.
+ ///
+ string Text {
+ get;
+ }
+
+ ///
+ /// An accessor property that returns the Words collection recognized in the text during an optical character recognition (OCR) operation.
+ ///
+ IWords Words { get; }
+
+ ///
+ /// Returns the number of characters in the recognized text.
+ ///
+ int NumChars { get; }
+
+ ///
+ /// Returns the number of words in the recognized text.
+ ///
+ int NumWords { get; }
+
+ ///
+ /// Returns the language identifier for the recognized text. Read-only Long.
+ ///
+ ModiLanguage Language { get; }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Modi/IMiRect.cs b/GreenshotOCRCommand/Modi/IMiRect.cs
new file mode 100644
index 000000000..060a838f9
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/IMiRect.cs
@@ -0,0 +1,48 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotOCRCommand.Modi
+{
+ ///
+ /// Represents a bounding rectangle in the optical character recognition (OCR) layout.
+ ///
+ public interface IMiRect : ICommon {
+ ///
+ /// The Bottom property represent the distance in pixels from the top edge of the containing image.
+ ///
+ int Bottom { get; }
+
+ ///
+ /// The Left property represent the distance in pixels from the left edge of the containing image.
+ ///
+ int Left { get; }
+
+ ///
+ /// The Right property represent the distance in pixels from the left edge of the containing image.
+ ///
+ int Right { get; }
+
+ ///
+ /// The Top property represent the distance in pixels from the top edge of the containing image.
+ ///
+ int Top { get; }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Modi/IMiRects.cs b/GreenshotOCRCommand/Modi/IMiRects.cs
new file mode 100644
index 000000000..b9ae99af5
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/IMiRects.cs
@@ -0,0 +1,38 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Collections;
+
+namespace GreenshotOCRCommand.Modi
+{
+ ///
+ /// Represents the collection of bounding rectangles in the optical character recognition (OCR) layout. A collection of MiRect objects.
+ ///
+ public interface IMiRects : ICommon, IEnumerable {
+ int Count {
+ get;
+ }
+ IMiRect this [int index] {
+ get;
+ }
+ new IEnumerator GetEnumerator();
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Modi/IWord.cs b/GreenshotOCRCommand/Modi/IWord.cs
new file mode 100644
index 000000000..f905c2617
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/IWord.cs
@@ -0,0 +1,65 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotOCRCommand.Modi
+{
+ ///
+ /// Represents a word recognized in the text during an optical character recognition (OCR) operation.
+ ///
+ public interface IWord : ICommon
+ {
+ ///
+ /// Returns the index of the specified word in the Words collection of the Layout or IMiSelectableItem object.
+ ///
+ long Id { get; }
+
+ ///
+ /// Returns the number of the region in the optical character recognition (OCR) layout where the word occurs.
+ ///
+ long RegionId { get; }
+
+ ///
+ /// Returns the number of the line in the optical character recognition (OCR) layout where the word occurs.
+ ///
+ long LineId { get; }
+
+ ///
+ /// Returns the recognized text as a Unicode string.
+ ///
+ string Text { get; }
+
+ ///
+ /// Returns the relative confidence factor reported by the optical character recognition (OCR) engine (on a scale of 0 to 999) after recognizing the specified word.
+ ///
+ short RecognitionConfidence { get; }
+
+ ///
+ /// Returns the index of the font used by the specified wordthis is the font that was recognized in the text during an optical character recognition (OCR) operation.
+ ///
+ long FontId { get; }
+
+ ///
+ /// Rectangles
+ ///
+ IMiRects Rects { get; }
+
+ }
+}
diff --git a/GreenshotOCRCommand/Modi/IWords.cs b/GreenshotOCRCommand/Modi/IWords.cs
new file mode 100644
index 000000000..41acfb0fe
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/IWords.cs
@@ -0,0 +1,43 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Collections;
+
+namespace GreenshotOCRCommand.Modi
+{
+ ///
+ /// The Words collection recognized in the text during an optical character recognition (OCR) operation.
+ ///
+ public interface IWords : ICommon, IEnumerable
+ {
+ int Count
+ {
+ get;
+ }
+
+ IWord this[int index]
+ {
+ get;
+ }
+
+ new IEnumerator GetEnumerator();
+ }
+}
diff --git a/GreenshotOCRCommand/Modi/ModiLanguage.cs b/GreenshotOCRCommand/Modi/ModiLanguage.cs
new file mode 100644
index 000000000..89e49ed5b
--- /dev/null
+++ b/GreenshotOCRCommand/Modi/ModiLanguage.cs
@@ -0,0 +1,48 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotOCRCommand.Modi
+{
+ public enum ModiLanguage {
+ CHINESE_SIMPLIFIED = 2052,
+ CHINESE_TRADITIONAL = 1028,
+ CZECH = 5,
+ DANISH = 6,
+ DUTCH = 19,
+ ENGLISH = 9,
+ FINNISH = 11,
+ FRENCH = 12,
+ GERMAN = 7,
+ GREEK = 8,
+ HUNGARIAN = 14,
+ ITALIAN = 16,
+ JAPANESE = 17,
+ KOREAN = 18,
+ NORWEGIAN = 20,
+ POLISH = 21,
+ PORTUGUESE = 22,
+ RUSSIAN = 25,
+ SPANISH = 10,
+ SWEDISH = 29,
+ TURKISH = 31,
+ SYSDEFAULT = 2048
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRCommand/Program.cs b/GreenshotOCRCommand/Program.cs
new file mode 100644
index 000000000..2bd757e8d
--- /dev/null
+++ b/GreenshotOCRCommand/Program.cs
@@ -0,0 +1,122 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using Greenshot.Interop;
+using GreenshotOCRCommand.Modi;
+
+namespace GreenshotOCRCommand {
+ public class Program {
+ private const string Usage = "<-c> | [language] [orientimage] [straightenImage]";
+ public static int Main(string[] args) {
+ if (args.Length == 0) {
+ Console.WriteLine(Usage);
+ return -1;
+ }
+ string filename = args[0];
+ ModiLanguage language = ModiLanguage.ENGLISH;
+ if (args.Length >= 2) {
+ language = (ModiLanguage)Enum.Parse(typeof(ModiLanguage), args[1]);
+ }
+ bool orientimage = true;
+ if (args.Length >= 3) {
+ orientimage = bool.Parse(args[2]);
+ }
+ bool straightenImage = true;
+ if (args.Length >= 4) {
+ straightenImage = bool.Parse(args[3]);
+ }
+ try {
+ if (File.Exists(filename) || "-c".Equals(filename))
+ {
+ using var document = COMWrapper.GetOrCreateInstance();
+ if (document == null) {
+ Console.WriteLine("MODI not installed");
+ return -2;
+ }
+ if ("-c".Equals(filename)) {
+ return 0;
+ }
+ document.Create(filename);
+ document.OCR(language, orientimage, straightenImage);
+ var modiImage = document.Images[0];
+ var layout = modiImage.Layout;
+ if (layout != null)
+ {
+#if DEBUG
+ if (layout.Words != null)
+ {
+ foreach (var word in ToEnumerable(layout.Words))
+ {
+ if (word.Rects != null)
+ {
+ foreach (var rect in ToEnumerable(word.Rects))
+ {
+ Debug.WriteLine($"Rect {rect.Left},{rect.Top},{rect.Right},{rect.Bottom} - Word {word.Text} : Confidence: {word.RecognitionConfidence}");
+ }
+ }
+ }
+ }
+#endif
+ if (layout.Text != null)
+ {
+ // For for BUG-1884:
+ // Although trim is done in the OCR Plugin, it does make sense in the command too.
+ Console.WriteLine(layout.Text.Trim());
+ }
+ }
+ document.Close(false);
+ return 0;
+ }
+ } catch (Exception ex) {
+ Console.WriteLine(ex.Message);
+ }
+ return -1;
+ }
+
+ ///
+ /// Helper method
+ ///
+ /// IEnumerable of IMiRect
+ private static IEnumerable ToEnumerable(IMiRects rects)
+ {
+ for (int i = 0; i < rects.Count; i++)
+ {
+ yield return rects[i];
+ }
+ }
+
+ ///
+ /// Helper method
+ ///
+ /// IEnumerable of IWord
+ private static IEnumerable ToEnumerable(IWords words)
+ {
+ for (int i = 0; i < words.Count; i++)
+ {
+ yield return words[i];
+ }
+ }
+ }
+}
diff --git a/GreenshotOCRCommand/Properties/AssemblyInfo.cs b/GreenshotOCRCommand/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..ff10a1848
--- /dev/null
+++ b/GreenshotOCRCommand/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die mit einer Assembly verknüpft sind.
+[assembly: AssemblyDescription("A small executable to OCR a bitmap")]
+
+// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
+// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
+// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
+[assembly: ComVisible(false)]
+
+// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
+[assembly: Guid("d7668e7e-3018-4d27-9aa0-21b1afade1b8")]
diff --git a/GreenshotOCRCommand/app.config b/GreenshotOCRCommand/app.config
new file mode 100644
index 000000000..2dcad202f
--- /dev/null
+++ b/GreenshotOCRCommand/app.config
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj b/GreenshotOCRPlugin/GreenshotOCRPlugin.csproj
similarity index 54%
rename from src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj
rename to GreenshotOCRPlugin/GreenshotOCRPlugin.csproj
index d0151b7c0..42e03e628 100644
--- a/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.csproj
+++ b/GreenshotOCRPlugin/GreenshotOCRPlugin.csproj
@@ -1,15 +1,16 @@
-
- none
- false
+
+
+ GreenshotOCRPlugin
+ GreenshotOCRPlugin
+
PreserveNewest
-
-
+
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-cs-CZ.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-cs-CZ.xml
new file mode 100644
index 000000000..eb225ae3e
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-cs-CZ.xml
@@ -0,0 +1,8 @@
+
+
+
+ Jazyk pro OCR
+ Orientaci obrázku
+ Srovnat obrázek
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-de-DE.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-de-DE.xml
new file mode 100644
index 000000000..a5743f569
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-de-DE.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ Sprache für OCR
+
+
+ Bild ausrichten
+
+
+ Bild glätten
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-en-US.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-en-US.xml
new file mode 100644
index 000000000..6412a02f6
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-en-US.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ Language for OCR
+
+
+ Orient image
+
+
+ Straighten image
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-fr-FR.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-fr-FR.xml
new file mode 100644
index 000000000..9e3c8126d
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-fr-FR.xml
@@ -0,0 +1,8 @@
+
+
+
+ Langage pour l'OCR
+ Orienter l'image
+ Redresser l'image
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-id-ID.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-id-ID.xml
new file mode 100644
index 000000000..e828076e2
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-id-ID.xml
@@ -0,0 +1,8 @@
+
+
+
+ Bahasa untuk OCR
+ Orientasikan gambar
+ Rapikan gambar
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-it-IT.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-it-IT.xml
new file mode 100644
index 000000000..670f233e2
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-it-IT.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ Lingua OCR
+
+
+ Orienta
+
+
+ Raddrizza
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-ja-JP.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-ja-JP.xml
new file mode 100644
index 000000000..c103704d4
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-ja-JP.xml
@@ -0,0 +1,8 @@
+
+
+
+ OCRの言語
+ 画像の向きを揃える
+ 画像の傾きを補正する
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-kab-DZ.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-kab-DZ.xml
new file mode 100644
index 000000000..3d884a724
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-kab-DZ.xml
@@ -0,0 +1,8 @@
+
+
+
+ Tutlayt i OCR
+ Wehhi tugna
+ Seggwem tugna
+
+
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-ko-KR.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-ko-KR.xml
new file mode 100644
index 000000000..e35bfe3b8
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-ko-KR.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ OCR 언어
+
+
+ 가로 이미지
+
+
+ 세로 이미지
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-lv-LV.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-lv-LV.xml
new file mode 100644
index 000000000..463aa73fb
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-lv-LV.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ OCR valoda
+
+
+ Pagriezt attēlu
+
+
+ Iztaisnot attēlu
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-nl-NL.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-nl-NL.xml
new file mode 100644
index 000000000..dac711d25
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-nl-NL.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ Taal voor OCR
+
+
+ Beeld richten
+
+
+ Beeld vereffenen
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-pl-PL.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-pl-PL.xml
new file mode 100644
index 000000000..37b40b65a
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-pl-PL.xml
@@ -0,0 +1,8 @@
+
+
+
+ Język dla OCR
+ Orientacja obrazu
+ Wyprostowanie obrazów
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-pt-PT.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-pt-PT.xml
new file mode 100644
index 000000000..ee55958d8
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-pt-PT.xml
@@ -0,0 +1,8 @@
+
+
+
+ Idioma para OCR
+ Orientar imagem
+ Endireitar imagem
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-ru-RU.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-ru-RU.xml
new file mode 100644
index 000000000..f2fffd34e
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-ru-RU.xml
@@ -0,0 +1,8 @@
+
+
+
+ Язык для OCR
+ Ориентация изображения
+ Выпрямление изображения
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-sk-SK.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-sk-SK.xml
new file mode 100644
index 000000000..0f61ec429
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-sk-SK.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ Jazyk pre OCR
+
+
+ Orientácia obrázku
+
+
+ Narovnať obrázok
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-sr-RS.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-sr-RS.xml
new file mode 100644
index 000000000..eaf252bd6
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-sr-RS.xml
@@ -0,0 +1,8 @@
+
+
+
+ Језик за препознавање знакова
+ Усмери слику
+ Исправи слику
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-sv-SE.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-sv-SE.xml
new file mode 100644
index 000000000..e59ce5e75
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-sv-SE.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ Språk för OCR
+
+
+ Orientera bild
+
+
+ Släta ut bild
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-uk-UA.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-uk-UA.xml
new file mode 100644
index 000000000..7a433eb4f
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-uk-UA.xml
@@ -0,0 +1,8 @@
+
+
+
+ Мова для ОРТ
+ Орієнтувати зображення
+ Випрямлення зображення
+
+
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-zh-CN.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-zh-CN.xml
new file mode 100644
index 000000000..ccd0e159a
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-zh-CN.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ OCR语言
+
+
+ 图像定位
+
+
+ 图像矫正
+
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/Languages/language_ocrplugin-zh-TW.xml b/GreenshotOCRPlugin/Languages/language_ocrplugin-zh-TW.xml
new file mode 100644
index 000000000..dce00e4cf
--- /dev/null
+++ b/GreenshotOCRPlugin/Languages/language_ocrplugin-zh-TW.xml
@@ -0,0 +1,8 @@
+
+
+
+ OCR 語言
+ 定向圖片
+ 拉直圖片
+
+
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/ModiLanguage.cs b/GreenshotOCRPlugin/ModiLanguage.cs
new file mode 100644
index 000000000..ae531b6ab
--- /dev/null
+++ b/GreenshotOCRPlugin/ModiLanguage.cs
@@ -0,0 +1,50 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace GreenshotOCRPlugin
+{
+ ///
+ /// Needed for the drop down, available languages for OCR
+ ///
+ public enum ModiLanguage {
+ CHINESE_SIMPLIFIED = 2052,
+ CHINESE_TRADITIONAL = 1028,
+ CZECH = 5,
+ DANISH = 6,
+ DUTCH = 19,
+ ENGLISH = 9,
+ FINNISH = 11,
+ FRENCH = 12,
+ GERMAN = 7,
+ GREEK = 8,
+ HUNGARIAN = 14,
+ ITALIAN = 16,
+ JAPANESE = 17,
+ KOREAN = 18,
+ NORWEGIAN = 20,
+ POLISH = 21,
+ PORTUGUESE = 22,
+ RUSSIAN = 25,
+ SPANISH = 10,
+ SWEDISH = 29,
+ TURKISH = 31,
+ SYSDEFAULT = 2048
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/OCRConfiguration.cs b/GreenshotOCRPlugin/OCRConfiguration.cs
new file mode 100644
index 000000000..bee6c33e1
--- /dev/null
+++ b/GreenshotOCRPlugin/OCRConfiguration.cs
@@ -0,0 +1,37 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using GreenshotPlugin.IniFile;
+
+namespace GreenshotOCRPlugin {
+ ///
+ /// Description of CoreConfiguration.
+ ///
+ [IniSection("OCR", Description="Greenshot OCR Plugin configuration")]
+ public class OCRConfiguration : IniSection {
+ [IniProperty("Language", Description="Language for OCR", DefaultValue="miLANG_ENGLISH")]
+ public string Language { get; set; }
+ [IniProperty("orientimage", Description="Orient image?", DefaultValue="true")]
+ public bool Orientimage { get; set; }
+ [IniProperty("straightenImage", Description="Straighten image?", DefaultValue="true")]
+ public bool StraightenImage { get; set; }
+ }
+}
diff --git a/GreenshotOCRPlugin/OCRDestination.cs b/GreenshotOCRPlugin/OCRDestination.cs
new file mode 100644
index 000000000..ee2dd975d
--- /dev/null
+++ b/GreenshotOCRPlugin/OCRDestination.cs
@@ -0,0 +1,60 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Drawing;
+using System.IO;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+
+namespace GreenshotOCRPlugin {
+ ///
+ /// Description of OCRDestination.
+ ///
+ public class OCRDestination : AbstractDestination {
+ private readonly OcrPlugin _plugin;
+
+ public override string Designation => "OCR";
+
+ public override string Description => "OCR";
+
+ public override Image DisplayIcon {
+ get {
+ string exePath = PluginUtils.GetExePath("MSPVIEW.EXE");
+ if (exePath != null && File.Exists(exePath)) {
+ return PluginUtils.GetCachedExeIcon(exePath, 0);
+ }
+ return null;
+ }
+ }
+
+ public OCRDestination(OcrPlugin plugin) {
+ _plugin = plugin;
+ }
+
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description)
+ {
+ ExportMade = _plugin.DoOcr(surface) != null
+ };
+ return exportInformation;
+ }
+ }
+}
diff --git a/GreenshotOCRPlugin/OCRForm.cs b/GreenshotOCRPlugin/OCRForm.cs
new file mode 100644
index 000000000..299bd990a
--- /dev/null
+++ b/GreenshotOCRPlugin/OCRForm.cs
@@ -0,0 +1,30 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using GreenshotPlugin.Controls;
+
+namespace GreenshotOCRPlugin {
+ ///
+ /// This class is needed for design-time resolving of the language files
+ ///
+ public class OcrForm : GreenshotForm {
+ }
+}
diff --git a/GreenshotOCRPlugin/OCRPlugin.cs b/GreenshotOCRPlugin/OCRPlugin.cs
new file mode 100644
index 000000000..96ad08135
--- /dev/null
+++ b/GreenshotOCRPlugin/OCRPlugin.cs
@@ -0,0 +1,206 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Windows.Forms;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Effects;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+//using Microsoft.Win32;
+
+namespace GreenshotOCRPlugin {
+ ///
+ /// OCR Plugin Greenshot
+ ///
+ [Plugin("Ocr", true)]
+ public class OcrPlugin : IGreenshotPlugin {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(OcrPlugin));
+ private string _ocrCommand;
+ private static OCRConfiguration _config;
+ private ToolStripMenuItem _ocrMenuItem = new ToolStripMenuItem();
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected void Dispose(bool disposing)
+ {
+ if (!disposing) return;
+ if (_ocrMenuItem == null) return;
+ _ocrMenuItem.Dispose();
+ _ocrMenuItem = null;
+ }
+
+ ///
+ /// Implementation of the IGreenshotPlugin.Initialize
+ ///
+ /// true if plugin is initialized, false if not (doesn't show)
+ public bool Initialize() {
+ Log.Debug("Initialize called");
+
+ var ocrDirectory = Path.GetDirectoryName(GetType().Assembly.Location);
+ if (ocrDirectory == null)
+ {
+ return false;
+ }
+ _ocrCommand = Path.Combine(ocrDirectory, "greenshotocrcommand.exe");
+
+ if (!HasModi()) {
+ Log.Warn("No MODI found!");
+ return false;
+ }
+ // Provide the IDestination
+ SimpleServiceProvider.Current.AddService(new OCRDestination(this));
+ // Load configuration
+ _config = IniConfig.GetIniSection();
+
+ if (_config.Language != null) {
+ _config.Language = _config.Language.Replace("miLANG_", string.Empty).Replace("_"," ");
+ }
+ return true;
+ }
+
+ ///
+ /// Implementation of the IGreenshotPlugin.Shutdown
+ ///
+ public void Shutdown() {
+ Log.Debug("Shutdown");
+ }
+
+ ///
+ /// Implementation of the IPlugin.Configure
+ ///
+ public void Configure() {
+ if (!HasModi()) {
+ MessageBox.Show("Sorry, is seems that Microsoft Office Document Imaging (MODI) is not installed, therefor the OCR Plugin cannot work.");
+ return;
+ }
+ SettingsForm settingsForm = new SettingsForm(Enum.GetNames(typeof(ModiLanguage)), _config);
+ DialogResult result = settingsForm.ShowDialog();
+ if (result == DialogResult.OK) {
+ // "Re"set hotkeys
+ IniConfig.Save();
+ }
+ }
+
+
+ private const int MinWidth = 130;
+ private const int MinHeight = 130;
+ ///
+ /// Handling of the CaptureTaken "event" from the ICaptureHost
+ /// We do the OCR here!
+ ///
+ /// Has the Image and the capture details
+ public string DoOcr(ISurface surface) {
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 0, true)
+ {
+ ReduceColors = true,
+ SaveBackgroundOnly = true
+ };
+ // We only want the background
+ // Force Grayscale output
+ outputSettings.Effects.Add(new GrayscaleEffect());
+
+ // Also we need to check the size, resize if needed to 130x130 this is the minimum
+ if (surface.Image.Width < MinWidth || surface.Image.Height < MinHeight) {
+ int addedWidth = MinWidth - surface.Image.Width;
+ if (addedWidth < 0) {
+ addedWidth = 0;
+ }
+ int addedHeight = MinHeight - surface.Image.Height;
+ if (addedHeight < 0) {
+ addedHeight = 0;
+ }
+ IEffect effect = new ResizeCanvasEffect(addedWidth / 2, addedWidth / 2, addedHeight / 2, addedHeight / 2);
+ outputSettings.Effects.Add(effect);
+ }
+ var filePath = ImageOutput.SaveToTmpFile(surface, outputSettings, null);
+
+ Log.Debug("Saved tmp file to: " + filePath);
+
+ string text = "";
+ try {
+ ProcessStartInfo processStartInfo = new ProcessStartInfo(_ocrCommand, "\"" + filePath + "\" " + _config.Language + " " + _config.Orientimage + " " + _config.StraightenImage)
+ {
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ UseShellExecute = false
+ };
+ using Process process = Process.Start(processStartInfo);
+ if (process != null)
+ {
+ process.WaitForExit(30 * 1000);
+ if (process.ExitCode == 0) {
+ text = process.StandardOutput.ReadToEnd();
+ }
+ }
+ } catch (Exception e) {
+ Log.Error("Error while calling Microsoft Office Document Imaging (MODI) to OCR: ", e);
+ } finally {
+ if (File.Exists(filePath)) {
+ Log.Debug("Cleaning up tmp file: " + filePath);
+ File.Delete(filePath);
+ }
+ }
+
+ if (string.IsNullOrEmpty(text)) {
+ Log.Info("No text returned");
+ return null;
+ }
+
+ // For for BUG-1884:
+ text = text.Trim();
+
+ try {
+ Log.DebugFormat("Pasting OCR Text to Clipboard: {0}", text);
+ // Paste to Clipboard (the Plugin currently doesn't have access to the ClipboardHelper from Greenshot
+ IDataObject ido = new DataObject();
+ ido.SetData(DataFormats.Text, true, text);
+ Clipboard.SetDataObject(ido, true);
+ } catch (Exception e) {
+ Log.Error("Problem pasting text to clipboard: ", e);
+ }
+ return text;
+ }
+
+ private bool HasModi() {
+ try
+ {
+ using Process process = Process.Start(_ocrCommand, "-c");
+ if (process != null)
+ {
+ process.WaitForExit();
+ return process.ExitCode == 0;
+ }
+ } catch(Exception e) {
+ Log.DebugFormat("Error trying to initiate MODI: {0}", e.Message);
+ }
+ Log.InfoFormat("No Microsoft Office Document Imaging (MODI) found, disabling OCR");
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOCRPlugin/SettingsForm.Designer.cs b/GreenshotOCRPlugin/SettingsForm.Designer.cs
new file mode 100644
index 000000000..5b0dfe877
--- /dev/null
+++ b/GreenshotOCRPlugin/SettingsForm.Designer.cs
@@ -0,0 +1,146 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace GreenshotOCRPlugin
+{
+ partial class SettingsForm
+ {
+ ///
+ /// Designer variable used to keep track of non-visual components.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Disposes resources used by the form.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing) {
+ if (components != null) {
+ components.Dispose();
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// This method is required for Windows Forms designer support.
+ /// Do not change the method contents inside the source code editor. The Forms designer might
+ /// not be able to load this method if it was changed manually.
+ ///
+ private void InitializeComponent()
+ {
+ this.comboBox_languages = new System.Windows.Forms.ComboBox();
+ this.checkBox_orientImage = new GreenshotPlugin.Controls.GreenshotCheckBox();
+ this.checkBox_straightenImage = new GreenshotPlugin.Controls.GreenshotCheckBox();
+ this.label_language = new GreenshotPlugin.Controls.GreenshotLabel();
+ this.buttonOK = new GreenshotPlugin.Controls.GreenshotButton();
+ this.buttonCancel = new GreenshotPlugin.Controls.GreenshotButton();
+ this.SuspendLayout();
+ //
+ // comboBox_languages
+ //
+ this.comboBox_languages.FormattingEnabled = true;
+ this.comboBox_languages.Items.AddRange(new object[] {
+ "English",
+ "Deutsch"});
+ this.comboBox_languages.Location = new System.Drawing.Point(74, 12);
+ this.comboBox_languages.Name = "comboBox_languages";
+ this.comboBox_languages.Size = new System.Drawing.Size(153, 21);
+ this.comboBox_languages.TabIndex = 1;
+ //
+ // checkBox_orientImage
+ //
+ this.checkBox_orientImage.LanguageKey = "ocr.orient_image";
+ this.checkBox_orientImage.Location = new System.Drawing.Point(13, 68);
+ this.checkBox_orientImage.Name = "checkBox_orientImage";
+ this.checkBox_orientImage.PropertyName = "orientimage";
+ this.checkBox_orientImage.SectionName = "OCR";
+ this.checkBox_orientImage.Size = new System.Drawing.Size(104, 24);
+ this.checkBox_orientImage.TabIndex = 3;
+ this.checkBox_orientImage.UseVisualStyleBackColor = true;
+ //
+ // checkBox_straightenImage
+ //
+ this.checkBox_straightenImage.LanguageKey = "ocr.straighten_image";
+ this.checkBox_straightenImage.Location = new System.Drawing.Point(13, 41);
+ this.checkBox_straightenImage.Name = "checkBox_straightenImage";
+ this.checkBox_straightenImage.PropertyName = "straightenImage";
+ this.checkBox_straightenImage.SectionName = "OCR";
+ this.checkBox_straightenImage.Size = new System.Drawing.Size(109, 24);
+ this.checkBox_straightenImage.TabIndex = 2;
+ this.checkBox_straightenImage.UseVisualStyleBackColor = true;
+ //
+ // label_language
+ //
+ this.label_language.LanguageKey = "ocr.language";
+ this.label_language.Location = new System.Drawing.Point(13, 15);
+ this.label_language.Name = "label_language";
+ this.label_language.Size = new System.Drawing.Size(55, 23);
+ this.label_language.TabIndex = 3;
+ //
+ // buttonOK
+ //
+ this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK;
+ this.buttonOK.LanguageKey = "OK";
+ this.buttonOK.Location = new System.Drawing.Point(12, 98);
+ this.buttonOK.Name = "buttonOK";
+ this.buttonOK.Size = new System.Drawing.Size(104, 23);
+ this.buttonOK.TabIndex = 4;
+ this.buttonOK.UseVisualStyleBackColor = true;
+ this.buttonOK.Click += new System.EventHandler(this.ButtonOKClick);
+ //
+ // buttonCancel
+ //
+ this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.buttonCancel.LanguageKey = "CANCEL";
+ this.buttonCancel.Location = new System.Drawing.Point(128, 98);
+ this.buttonCancel.Name = "buttonCancel";
+ this.buttonCancel.Size = new System.Drawing.Size(104, 23);
+ this.buttonCancel.TabIndex = 5;
+ this.buttonCancel.UseVisualStyleBackColor = true;
+ //
+ // SettingsForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.ClientSize = new System.Drawing.Size(244, 135);
+ this.Controls.Add(this.buttonCancel);
+ this.Controls.Add(this.buttonOK);
+ this.Controls.Add(this.label_language);
+ this.Controls.Add(this.checkBox_straightenImage);
+ this.Controls.Add(this.checkBox_orientImage);
+ this.Controls.Add(this.comboBox_languages);
+ this.LanguageKey = "settings_title";
+ this.Name = "SettingsForm";
+ this.ResumeLayout(false);
+
+ }
+ private GreenshotPlugin.Controls.GreenshotLabel label_language;
+ private GreenshotPlugin.Controls.GreenshotButton buttonCancel;
+ private GreenshotPlugin.Controls.GreenshotButton buttonOK;
+ private GreenshotPlugin.Controls.GreenshotCheckBox checkBox_orientImage;
+ private GreenshotPlugin.Controls.GreenshotCheckBox checkBox_straightenImage;
+ private System.Windows.Forms.ComboBox comboBox_languages;
+ }
+}
diff --git a/GreenshotOCRPlugin/SettingsForm.cs b/GreenshotOCRPlugin/SettingsForm.cs
new file mode 100644
index 000000000..b2c88629d
--- /dev/null
+++ b/GreenshotOCRPlugin/SettingsForm.cs
@@ -0,0 +1,65 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+
+namespace GreenshotOCRPlugin {
+ ///
+ /// Description of SettingsForm.
+ ///
+ public partial class SettingsForm : OcrForm {
+ private readonly OCRConfiguration config;
+
+ public SettingsForm(string [] languages, OCRConfiguration config) {
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ this.config = config;
+ InitializeComponent();
+ AcceptButton = buttonOK;
+ CancelButton = buttonCancel;
+
+ comboBox_languages.Items.Clear();
+ int index=0;
+
+ // Preventing Tracker #3234560, although this should not happen...
+ string languageFromConfig = "ENGLISH";
+ if (config.Language != null) {
+ languageFromConfig = config.Language;
+ }
+ foreach(string availableLanguage in languages) {
+ string displayLanguage = availableLanguage.Substring(0, 1).ToUpper() + availableLanguage.Substring(1).ToLower();
+ comboBox_languages.Items.Add(displayLanguage);
+ if (availableLanguage.Equals(languageFromConfig, StringComparison.CurrentCultureIgnoreCase)) {
+ comboBox_languages.SelectedIndex = index;
+ }
+ index++;
+ }
+ }
+
+ private void ButtonOKClick(object sender, EventArgs e) {
+ string selectedString = (string) comboBox_languages.SelectedItem;
+ if (selectedString != null) {
+ config.Language = selectedString.ToUpper();
+ }
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Office/Com/DisposableCom.cs b/GreenshotOfficePlugin/Com/DisposableCom.cs
similarity index 92%
rename from src/Greenshot.Plugin.Office/Com/DisposableCom.cs
rename to GreenshotOfficePlugin/Com/DisposableCom.cs
index e722f48e3..1fc17a78b 100644
--- a/src/Greenshot.Plugin.Office/Com/DisposableCom.cs
+++ b/GreenshotOfficePlugin/Com/DisposableCom.cs
@@ -1,7 +1,6 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace Greenshot.Plugin.Office.Com
+namespace GreenshotOfficePlugin.Com
{
///
/// A factory for IDisposableCom
diff --git a/src/Greenshot.Plugin.Office/Com/DisposableComImplementation.cs b/GreenshotOfficePlugin/Com/DisposableComImplementation.cs
similarity index 94%
rename from src/Greenshot.Plugin.Office/Com/DisposableComImplementation.cs
rename to GreenshotOfficePlugin/Com/DisposableComImplementation.cs
index 572de52d8..e7a52baac 100644
--- a/src/Greenshot.Plugin.Office/Com/DisposableComImplementation.cs
+++ b/GreenshotOfficePlugin/Com/DisposableComImplementation.cs
@@ -4,7 +4,7 @@
using System;
using System.Runtime.InteropServices;
-namespace Greenshot.Plugin.Office.Com
+namespace GreenshotOfficePlugin.Com
{
///
/// Implementation of the IDisposableCom, this is internal to prevent other code to use it directly
@@ -38,7 +38,6 @@ namespace Greenshot.Plugin.Office.Com
{
return;
}
-
// Do not catch an exception from this.
// You may want to remove these guards depending on
// what you think the semantics should be.
@@ -46,7 +45,6 @@ namespace Greenshot.Plugin.Office.Com
{
Marshal.ReleaseComObject(ComObject);
}
-
ComObject = default;
}
}
diff --git a/src/Greenshot.Plugin.Office/Com/IDisposableCom.cs b/GreenshotOfficePlugin/Com/IDisposableCom.cs
similarity index 90%
rename from src/Greenshot.Plugin.Office/Com/IDisposableCom.cs
rename to GreenshotOfficePlugin/Com/IDisposableCom.cs
index d99b6f4c6..6bf1327c7 100644
--- a/src/Greenshot.Plugin.Office/Com/IDisposableCom.cs
+++ b/GreenshotOfficePlugin/Com/IDisposableCom.cs
@@ -3,7 +3,7 @@
using System;
-namespace Greenshot.Plugin.Office.Com
+namespace GreenshotOfficePlugin.Com
{
///
/// A simple com wrapper which helps with "using"
diff --git a/src/Greenshot.Plugin.Office/Com/Ole32Api.cs b/GreenshotOfficePlugin/Com/Ole32Api.cs
similarity index 51%
rename from src/Greenshot.Plugin.Office/Com/Ole32Api.cs
rename to GreenshotOfficePlugin/Com/Ole32Api.cs
index 1a57aa6c9..783f48d0d 100644
--- a/src/Greenshot.Plugin.Office/Com/Ole32Api.cs
+++ b/GreenshotOfficePlugin/Com/Ole32Api.cs
@@ -3,10 +3,10 @@
using System;
using System.Runtime.InteropServices;
-using Dapplo.Windows.Common.Enums;
-using Dapplo.Windows.Common.Extensions;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Core.Enums;
-namespace Greenshot.Plugin.Office.Com
+namespace GreenshotOfficePlugin.Com
{
///
/// This provides an API for OLE32
@@ -24,10 +24,24 @@ namespace Greenshot.Plugin.Office.Com
{
return clsId;
}
-
return clsId;
}
+ ///
+ /// This converts a clsid (Class ID) into a ProgID (program ID)
+ ///
+ /// Guid with the clsid (Class ID)
+ /// string with the progid
+ public static string ProgIdFromClassId(Guid clsId)
+ {
+ if (ProgIDFromCLSID(ref clsId, out string progId).Succeeded())
+ {
+ return progId;
+ }
+
+ return null;
+ }
+
///
/// See more here
///
@@ -36,5 +50,14 @@ namespace Greenshot.Plugin.Office.Com
/// HResult
[DllImport("ole32.dll", ExactSpelling = true)]
private static extern HResult CLSIDFromProgID([In] [MarshalAs(UnmanagedType.LPWStr)] string progId, [Out] out Guid clsId);
+
+ ///
+ /// See more here
+ ///
+ /// Guid The CLSID for which the ProgID is to be requested.
+ /// string the ProgID string. The string that represents clsid includes enclosing braces.
+ /// HResult
+ [DllImport("ole32.dll")]
+ private static extern HResult ProgIDFromCLSID([In] ref Guid clsId, [MarshalAs(UnmanagedType.LPWStr)] out string lplpszProgId);
}
-}
\ No newline at end of file
+}
diff --git a/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs b/GreenshotOfficePlugin/Com/OleAut32Api.cs
similarity index 89%
rename from src/Greenshot.Plugin.Office/Com/OleAut32Api.cs
rename to GreenshotOfficePlugin/Com/OleAut32Api.cs
index ea0f1b0c9..6a3e4284c 100644
--- a/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs
+++ b/GreenshotOfficePlugin/Com/OleAut32Api.cs
@@ -3,10 +3,10 @@
using System;
using System.Runtime.InteropServices;
-using Dapplo.Windows.Common.Enums;
-using Dapplo.Windows.Common.Extensions;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Core.Enums;
-namespace Greenshot.Plugin.Office.Com
+namespace GreenshotOfficePlugin.Com
{
///
/// API for OLEAUT32
@@ -23,7 +23,7 @@ namespace Greenshot.Plugin.Office.Com
{
if (GetActiveObject(ref clsId, IntPtr.Zero, out object comObject).Succeeded())
{
- return DisposableCom.Create((T) comObject);
+ return DisposableCom.Create((T)comObject);
}
return null;
@@ -51,4 +51,4 @@ namespace Greenshot.Plugin.Office.Com
[DllImport("oleaut32.dll")]
private static extern HResult GetActiveObject(ref Guid rclsId, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out object ppunk);
}
-}
\ No newline at end of file
+}
diff --git a/GreenshotOfficePlugin/Destinations/ExcelDestination.cs b/GreenshotOfficePlugin/Destinations/ExcelDestination.cs
new file mode 100644
index 000000000..93ea6ed85
--- /dev/null
+++ b/GreenshotOfficePlugin/Destinations/ExcelDestination.cs
@@ -0,0 +1,97 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Text.RegularExpressions;
+using GreenshotOfficePlugin.OfficeExport;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotOfficePlugin.Destinations {
+ ///
+ /// Description of PowerpointDestination.
+ ///
+ public class ExcelDestination : AbstractDestination {
+ private const int IconApplication = 0;
+ private const int IconWorkbook = 1;
+ private static readonly string ExePath;
+ private readonly string _workbookName;
+
+ static ExcelDestination() {
+ ExePath = PluginUtils.GetExePath("EXCEL.EXE");
+ if (ExePath != null && File.Exists(ExePath)) {
+ WindowDetails.AddProcessToExcludeFromFreeze("excel");
+ } else {
+ ExePath = null;
+ }
+ }
+
+ public ExcelDestination() {
+ }
+
+ public ExcelDestination(string workbookName) {
+ _workbookName = workbookName;
+ }
+
+ public override string Designation => "Excel";
+
+ public override string Description => _workbookName ?? "Microsoft Excel";
+
+ public override int Priority => 5;
+
+ public override bool IsDynamic => true;
+
+ public override bool IsActive => base.IsActive && ExePath != null;
+
+ public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_workbookName) ? IconWorkbook : IconApplication);
+
+ public override IEnumerable DynamicDestinations() {
+ foreach (string workbookName in ExcelExporter.GetWorkbooks()) {
+ yield return new ExcelDestination(workbookName);
+ }
+ }
+
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description);
+ bool createdFile = false;
+ string imageFile = captureDetails.Filename;
+ if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
+ imageFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
+ createdFile = true;
+ }
+ if (_workbookName != null) {
+ ExcelExporter.InsertIntoExistingWorkbook(_workbookName, imageFile, surface.Image.Size);
+ } else {
+ ExcelExporter.InsertIntoNewWorkbook(imageFile, surface.Image.Size);
+ }
+ exportInformation.ExportMade = true;
+ ProcessExport(exportInformation, surface);
+ // Cleanup imageFile if we created it here, so less tmp-files are generated and left
+ if (createdFile) {
+ ImageOutput.DeleteNamedTmpFile(imageFile);
+ }
+ return exportInformation;
+ }
+ }
+}
diff --git a/GreenshotOfficePlugin/Destinations/OneNoteDestination.cs b/GreenshotOfficePlugin/Destinations/OneNoteDestination.cs
new file mode 100644
index 000000000..77d3c0a03
--- /dev/null
+++ b/GreenshotOfficePlugin/Destinations/OneNoteDestination.cs
@@ -0,0 +1,124 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using GreenshotOfficePlugin.OfficeExport;
+using GreenshotOfficePlugin.OfficeExport.Entities;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+
+namespace GreenshotOfficePlugin.Destinations {
+ public class OneNoteDestination : AbstractDestination {
+ private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WordDestination));
+ private const int ICON_APPLICATION = 0;
+ public const string DESIGNATION = "OneNote";
+ private static readonly string exePath;
+ private readonly OneNotePage page;
+ private readonly OneNoteExporter _oneNoteExporter = new OneNoteExporter();
+
+ static OneNoteDestination() {
+ exePath = PluginUtils.GetExePath("ONENOTE.EXE");
+ if (exePath != null && File.Exists(exePath)) {
+ WindowDetails.AddProcessToExcludeFromFreeze("onenote");
+ } else {
+ exePath = null;
+ }
+ }
+
+ public OneNoteDestination() {
+
+ }
+
+ public OneNoteDestination(OneNotePage page) {
+ this.page = page;
+ }
+
+ public override string Designation {
+ get {
+ return DESIGNATION;
+ }
+ }
+
+ public override string Description {
+ get {
+ if (page == null) {
+ return "Microsoft OneNote";
+ } else {
+ return page.DisplayName;
+ }
+ }
+ }
+
+ public override int Priority {
+ get {
+ return 4;
+ }
+ }
+
+ public override bool IsDynamic {
+ get {
+ return true;
+ }
+ }
+
+ public override bool IsActive {
+ get {
+ return base.IsActive && exePath != null;
+ }
+ }
+
+ public override Image DisplayIcon {
+ get {
+ return PluginUtils.GetCachedExeIcon(exePath, ICON_APPLICATION);
+ }
+ }
+
+ public override IEnumerable DynamicDestinations() {
+ foreach (OneNotePage page in _oneNoteExporter.GetPages()) {
+ yield return new OneNoteDestination(page);
+ }
+ }
+
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description);
+
+ if (page == null) {
+ try {
+ exportInformation.ExportMade = _oneNoteExporter.ExportToNewPage(surface);
+ } catch(Exception ex) {
+ exportInformation.ErrorMessage = ex.Message;
+ LOG.Error(ex);
+ }
+ } else {
+ try {
+ exportInformation.ExportMade = _oneNoteExporter.ExportToPage(surface, page);
+ } catch(Exception ex) {
+ exportInformation.ErrorMessage = ex.Message;
+ LOG.Error(ex);
+ }
+ }
+ return exportInformation;
+ }
+ }
+}
diff --git a/GreenshotOfficePlugin/Destinations/OutlookDestination.cs b/GreenshotOfficePlugin/Destinations/OutlookDestination.cs
new file mode 100644
index 000000000..ac88ec091
--- /dev/null
+++ b/GreenshotOfficePlugin/Destinations/OutlookDestination.cs
@@ -0,0 +1,165 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using GreenshotOfficePlugin.OfficeExport;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+using Microsoft.Office.Interop.Outlook;
+
+namespace GreenshotOfficePlugin.Destinations {
+ ///
+ /// Description of OutlookDestination.
+ ///
+ public class OutlookDestination : AbstractDestination {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(OutlookDestination));
+ private const int IconApplication = 0;
+ private const int IconMeeting = 2;
+
+ private static readonly Image MailIcon = GreenshotResources.GetImage("Email.Image");
+ private static readonly OfficeConfiguration OfficeConfig = IniConfig.GetIniSection();
+ private static readonly string ExePath;
+ private static readonly bool IsActiveFlag;
+ private const string MapiClient = "Microsoft Outlook";
+ private readonly string _outlookInspectorCaption;
+ private readonly OlObjectClass _outlookInspectorType;
+ private readonly OutlookEmailExporter _outlookEmailExporter = new OutlookEmailExporter();
+
+ static OutlookDestination() {
+ if (EmailConfigHelper.HasOutlook()) {
+ IsActiveFlag = true;
+ }
+ ExePath = PluginUtils.GetExePath("OUTLOOK.EXE");
+ if (ExePath != null && File.Exists(ExePath)) {
+ WindowDetails.AddProcessToExcludeFromFreeze("outlook");
+ } else {
+ ExePath = null;
+ }
+ if (ExePath == null) {
+ IsActiveFlag = false;
+ }
+ }
+
+ public OutlookDestination() {
+ }
+
+ public OutlookDestination(string outlookInspectorCaption, OlObjectClass outlookInspectorType) {
+ _outlookInspectorCaption = outlookInspectorCaption;
+ _outlookInspectorType = outlookInspectorType;
+ }
+
+ public override string Designation => "Outlook";
+
+ public override string Description => _outlookInspectorCaption ?? MapiClient;
+
+ public override int Priority => 3;
+
+ public override bool IsActive => base.IsActive && IsActiveFlag;
+
+ public override bool IsDynamic => true;
+
+ public override Keys EditorShortcutKeys => Keys.Control | Keys.E;
+
+ public override Image DisplayIcon {
+ get
+ {
+ if (_outlookInspectorCaption == null)
+ {
+ return PluginUtils.GetCachedExeIcon(ExePath, IconApplication);
+ }
+ if (OlObjectClass.olAppointment.Equals(_outlookInspectorType)) {
+ // Make sure we loaded the icon, maybe the configuration has been changed!
+ return PluginUtils.GetCachedExeIcon(ExePath, IconMeeting);
+ }
+ return MailIcon;
+ }
+ }
+
+ public override IEnumerable DynamicDestinations() {
+ IDictionary inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets();
+ if (inspectorCaptions != null) {
+ foreach (string inspectorCaption in inspectorCaptions.Keys) {
+ yield return new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]);
+ }
+ }
+ }
+
+ ///
+ /// Export the capture to outlook
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description);
+ // Outlook logic
+ string tmpFile = captureDetails.Filename;
+ if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
+ tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
+ } else {
+ Log.InfoFormat("Using already available file: {0}", tmpFile);
+ }
+
+ // Create a attachment name for the image
+ string attachmentName = captureDetails.Title;
+ if (!string.IsNullOrEmpty(attachmentName)) {
+ attachmentName = attachmentName.Trim();
+ }
+ // Set default if non is set
+ if (string.IsNullOrEmpty(attachmentName)) {
+ attachmentName = "Greenshot Capture";
+ }
+ // Make sure it's "clean" so it doesn't corrupt the header
+ attachmentName = Regex.Replace(attachmentName, @"[^\x20\d\w]", string.Empty);
+
+ if (_outlookInspectorCaption != null) {
+ _outlookEmailExporter.ExportToInspector(_outlookInspectorCaption, tmpFile, attachmentName);
+ exportInformation.ExportMade = true;
+ } else {
+ if (!manuallyInitiated) {
+ var inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets();
+ if (inspectorCaptions != null && inspectorCaptions.Count > 0) {
+ var destinations = new List
+ {
+ new OutlookDestination()
+ };
+ foreach (string inspectorCaption in inspectorCaptions.Keys) {
+ destinations.Add(new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]));
+ }
+ // Return the ExportInformation from the picker without processing, as this indirectly comes from us self
+ return ShowPickerMenu(false, surface, captureDetails, destinations);
+ }
+ } else {
+ exportInformation.ExportMade = _outlookEmailExporter.ExportToOutlook(OfficeConfig.OutlookEmailFormat, tmpFile, FilenameHelper.FillPattern(OfficeConfig.EmailSubjectPattern, captureDetails, false), attachmentName, OfficeConfig.EmailTo, OfficeConfig.EmailCC, OfficeConfig.EmailBCC, null);
+ }
+ }
+ ProcessExport(exportInformation, surface);
+ return exportInformation;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotOfficePlugin/Destinations/PowerpointDestination.cs b/GreenshotOfficePlugin/Destinations/PowerpointDestination.cs
new file mode 100644
index 000000000..1000fb7d9
--- /dev/null
+++ b/GreenshotOfficePlugin/Destinations/PowerpointDestination.cs
@@ -0,0 +1,123 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using GreenshotOfficePlugin.OfficeExport;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotOfficePlugin.Destinations {
+ ///
+ /// Description of PowerpointDestination.
+ ///
+ public class PowerpointDestination : AbstractDestination {
+ private const int IconApplication = 0;
+ private const int IconPresentation = 1;
+
+ private static readonly string ExePath;
+ private readonly string _presentationName;
+ private readonly PowerpointExporter _powerpointExporter = new PowerpointExporter();
+
+ static PowerpointDestination() {
+ ExePath = PluginUtils.GetExePath("POWERPNT.EXE");
+ if (ExePath != null && File.Exists(ExePath)) {
+ WindowDetails.AddProcessToExcludeFromFreeze("powerpnt");
+ } else {
+ ExePath = null;
+ }
+ }
+
+ public PowerpointDestination() {
+ }
+
+ public PowerpointDestination(string presentationName) {
+ _presentationName = presentationName;
+ }
+
+ public override string Designation => "Powerpoint";
+
+ public override string Description {
+ get
+ {
+ if (_presentationName == null) {
+ return "Microsoft Powerpoint";
+ }
+ return _presentationName;
+ }
+ }
+
+ public override int Priority => 4;
+
+ public override bool IsDynamic => true;
+
+ public override bool IsActive => base.IsActive && ExePath != null;
+
+ public override Image DisplayIcon {
+ get {
+ if (!string.IsNullOrEmpty(_presentationName)) {
+ return PluginUtils.GetCachedExeIcon(ExePath, IconPresentation);
+ }
+
+ return PluginUtils.GetCachedExeIcon(ExePath, IconApplication);
+ }
+ }
+
+ public override IEnumerable DynamicDestinations() {
+ foreach (string presentationName in _powerpointExporter.GetPowerpointPresentations()) {
+ yield return new PowerpointDestination(presentationName);
+ }
+ }
+
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description);
+ string tmpFile = captureDetails.Filename;
+ Size imageSize = Size.Empty;
+ if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
+ tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
+ imageSize = surface.Image.Size;
+ }
+ if (_presentationName != null) {
+ exportInformation.ExportMade = _powerpointExporter.ExportToPresentation(_presentationName, tmpFile, imageSize, captureDetails.Title);
+ } else {
+ if (!manuallyInitiated) {
+ var presentations = _powerpointExporter.GetPowerpointPresentations().ToList();
+ if (presentations != null && presentations.Count > 0) {
+ var destinations = new List {new PowerpointDestination()};
+ foreach (string presentation in presentations) {
+ destinations.Add(new PowerpointDestination(presentation));
+ }
+ // Return the ExportInformation from the picker without processing, as this indirectly comes from us self
+ return ShowPickerMenu(false, surface, captureDetails, destinations);
+ }
+ } else if (!exportInformation.ExportMade) {
+ exportInformation.ExportMade = _powerpointExporter.InsertIntoNewPresentation(tmpFile, imageSize, captureDetails.Title);
+ }
+ }
+ ProcessExport(exportInformation, surface);
+ return exportInformation;
+ }
+ }
+}
diff --git a/GreenshotOfficePlugin/Destinations/WordDestination.cs b/GreenshotOfficePlugin/Destinations/WordDestination.cs
new file mode 100644
index 000000000..a8a80893d
--- /dev/null
+++ b/GreenshotOfficePlugin/Destinations/WordDestination.cs
@@ -0,0 +1,131 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using GreenshotOfficePlugin.OfficeExport;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotOfficePlugin.Destinations {
+ ///
+ /// Description of EmailDestination.
+ ///
+ public class WordDestination : AbstractDestination {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(WordDestination));
+ private const int IconApplication = 0;
+ private const int IconDocument = 1;
+ private static readonly string ExePath;
+ private readonly string _documentCaption;
+ private readonly WordExporter _wordExporter = new WordExporter();
+ static WordDestination() {
+ ExePath = PluginUtils.GetExePath("WINWORD.EXE");
+ if (ExePath != null && !File.Exists(ExePath)) {
+ ExePath = null;
+ }
+ }
+
+ public WordDestination() {
+
+ }
+
+ public WordDestination(string wordCaption) {
+ _documentCaption = wordCaption;
+ }
+
+ public override string Designation => "Word";
+
+ public override string Description => _documentCaption ?? "Microsoft Word";
+
+ public override int Priority => 4;
+
+ public override bool IsDynamic => true;
+
+ public override bool IsActive => base.IsActive && ExePath != null;
+
+ public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_documentCaption) ? IconDocument : IconApplication);
+
+ public override IEnumerable DynamicDestinations() {
+ foreach (string wordCaption in _wordExporter.GetWordDocuments()) {
+ yield return new WordDestination(wordCaption);
+ }
+ }
+
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description);
+ string tmpFile = captureDetails.Filename;
+ if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) {
+ tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat());
+ }
+ if (_documentCaption != null) {
+ try {
+ _wordExporter.InsertIntoExistingDocument(_documentCaption, tmpFile);
+ exportInformation.ExportMade = true;
+ } catch (Exception) {
+ try {
+ _wordExporter.InsertIntoExistingDocument(_documentCaption, tmpFile);
+ exportInformation.ExportMade = true;
+ } catch (Exception ex) {
+ Log.Error(ex);
+ // TODO: Change to general logic in ProcessExport
+ surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description));
+ }
+ }
+ } else {
+ if (!manuallyInitiated) {
+ var documents = _wordExporter.GetWordDocuments().ToList();
+ if (documents != null && documents.Count > 0) {
+ var destinations = new List
+ {
+ new WordDestination()
+ };
+ foreach (string document in documents) {
+ destinations.Add(new WordDestination(document));
+ }
+ // Return the ExportInformation from the picker without processing, as this indirectly comes from us self
+ return ShowPickerMenu(false, surface, captureDetails, destinations);
+ }
+ }
+ try {
+ _wordExporter.InsertIntoNewDocument(tmpFile, null, null);
+ exportInformation.ExportMade = true;
+ } catch(Exception) {
+ // Retry once, just in case
+ try {
+ _wordExporter.InsertIntoNewDocument(tmpFile, null, null);
+ exportInformation.ExportMade = true;
+ } catch (Exception ex) {
+ Log.Error(ex);
+ // TODO: Change to general logic in ProcessExport
+ surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description));
+ }
+ }
+ }
+ ProcessExport(exportInformation, surface);
+ return exportInformation;
+ }
+ }
+}
diff --git a/GreenshotOfficePlugin/GlobalSuppressions.cs b/GreenshotOfficePlugin/GlobalSuppressions.cs
new file mode 100644
index 000000000..882507f6b
Binary files /dev/null and b/GreenshotOfficePlugin/GlobalSuppressions.cs differ
diff --git a/src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj b/GreenshotOfficePlugin/GreenshotOfficePlugin.csproj
similarity index 92%
rename from src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj
rename to GreenshotOfficePlugin/GreenshotOfficePlugin.csproj
index 982b86833..f6f07e98d 100644
--- a/src/Greenshot.Plugin.Office/Greenshot.Plugin.Office.csproj
+++ b/GreenshotOfficePlugin/GreenshotOfficePlugin.csproj
@@ -1,15 +1,17 @@
-
- none
- false
+
+
+ GreenshotOfficePlugin
+ GreenshotOfficePlugin
+
PreserveNewest
-
+
@@ -82,23 +84,23 @@
-
+ trueruntime
-
+ trueruntime
-
+ trueruntime
-
+ trueruntime
-
+ trueruntime
diff --git a/src/Greenshot.Plugin.Office/Languages/language_office-fr-FR.ini b/GreenshotOfficePlugin/Languages/language_officeplugin-fr-FR.ini
similarity index 100%
rename from src/Greenshot.Plugin.Office/Languages/language_office-fr-FR.ini
rename to GreenshotOfficePlugin/Languages/language_officeplugin-fr-FR.ini
diff --git a/GreenshotOfficePlugin/OfficeConfiguration.cs b/GreenshotOfficePlugin/OfficeConfiguration.cs
new file mode 100644
index 000000000..3971e5cd4
--- /dev/null
+++ b/GreenshotOfficePlugin/OfficeConfiguration.cs
@@ -0,0 +1,54 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using GreenshotOfficePlugin.OfficeInterop;
+using GreenshotPlugin.IniFile;
+using Microsoft.Office.Interop.PowerPoint;
+
+namespace GreenshotOfficePlugin {
+
+ ///
+ /// Description of CoreConfiguration.
+ ///
+ [IniSection("Office", Description="Greenshot Office configuration")]
+ public class OfficeConfiguration : IniSection {
+ [IniProperty("OutlookEmailFormat", Description = "Default type for emails. (Text, HTML)", DefaultValue = "HTML")]
+ public EmailFormat OutlookEmailFormat { get; set; }
+
+ [IniProperty("EmailSubjectPattern", Description = "Email subject pattern, works like the OutputFileFilenamePattern", DefaultValue = "${title}")]
+ public string EmailSubjectPattern { get; set; }
+ [IniProperty("EmailTo", Description = "Default value for the to in emails that are created", DefaultValue = "")]
+ public string EmailTo { get; set; }
+ [IniProperty("EmailCC", Description = "Default value for the CC in emails that are created", DefaultValue = "")]
+ public string EmailCC { get; set; }
+ [IniProperty("EmailBCC", Description = "Default value for the BCC in emails that are created", DefaultValue = "")]
+ public string EmailBCC { get; set; }
+ [IniProperty("OutlookAllowExportInMeetings", Description = "For Outlook: Allow export in meeting items", DefaultValue = "False")]
+ public bool OutlookAllowExportInMeetings { get; set; }
+ [IniProperty("WordLockAspectRatio", Description = "For Word: Lock the aspect ratio of the image", DefaultValue = "True")]
+ public bool WordLockAspectRatio { get; set; }
+ [IniProperty("PowerpointLockAspectRatio", Description = "For Powerpoint: Lock the aspect ratio of the image", DefaultValue = "True")]
+ public bool PowerpointLockAspectRatio { get; set; }
+ [IniProperty("PowerpointSlideLayout", Description = "For Powerpoint: Slide layout, changing this to a wrong value will fallback on ppLayoutBlank!!", DefaultValue = "ppLayoutPictureWithCaption")]
+ public PpSlideLayout PowerpointSlideLayout { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNoteNotebook.cs b/GreenshotOfficePlugin/OfficeExport/Entities/OneNoteNotebook.cs
similarity index 79%
rename from src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNoteNotebook.cs
rename to GreenshotOfficePlugin/OfficeExport/Entities/OneNoteNotebook.cs
index e161c6bcd..75c2f23d3 100644
--- a/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNoteNotebook.cs
+++ b/GreenshotOfficePlugin/OfficeExport/Entities/OneNoteNotebook.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,9 +15,9 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
-namespace Greenshot.Plugin.Office.OfficeExport.Entities
+namespace GreenshotOfficePlugin.OfficeExport.Entities
{
///
/// Container for transporting notebook information
diff --git a/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNotePage.cs b/GreenshotOfficePlugin/OfficeExport/Entities/OneNotePage.cs
similarity index 83%
rename from src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNotePage.cs
rename to GreenshotOfficePlugin/OfficeExport/Entities/OneNotePage.cs
index e6fcd4bff..b9fd2c4a9 100644
--- a/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNotePage.cs
+++ b/GreenshotOfficePlugin/OfficeExport/Entities/OneNotePage.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,9 +15,9 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
-namespace Greenshot.Plugin.Office.OfficeExport.Entities
+namespace GreenshotOfficePlugin.OfficeExport.Entities
{
///
/// Container for transporting Page information
@@ -34,7 +34,6 @@ namespace Greenshot.Plugin.Office.OfficeExport.Entities
{
return string.Format("{0} / {1}", Parent.Name, Name);
}
-
return string.Format("{0} / {1} / {2}", Parent.Parent.Name, Parent.Name, Name);
}
}
diff --git a/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNoteSection.cs b/GreenshotOfficePlugin/OfficeExport/Entities/OneNoteSection.cs
similarity index 80%
rename from src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNoteSection.cs
rename to GreenshotOfficePlugin/OfficeExport/Entities/OneNoteSection.cs
index 93fd20e63..e02908c11 100644
--- a/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNoteSection.cs
+++ b/GreenshotOfficePlugin/OfficeExport/Entities/OneNoteSection.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,9 +15,9 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
-namespace Greenshot.Plugin.Office.OfficeExport.Entities
+namespace GreenshotOfficePlugin.OfficeExport.Entities
{
///
/// Container for transporting section information
diff --git a/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs b/GreenshotOfficePlugin/OfficeExport/ExcelExporter.cs
similarity index 90%
rename from src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs
rename to GreenshotOfficePlugin/OfficeExport/ExcelExporter.cs
index cfffff5f6..71939296e 100644
--- a/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs
+++ b/GreenshotOfficePlugin/OfficeExport/ExcelExporter.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,19 +15,19 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
using System;
using System.Collections.Generic;
using System.Drawing;
-using Dapplo.Windows.User32;
-using Greenshot.Plugin.Office.Com;
-using Greenshot.Plugin.Office.OfficeInterop;
+using GreenshotOfficePlugin.Com;
+using GreenshotOfficePlugin.OfficeInterop;
+using GreenshotPlugin.UnmanagedHelpers;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.Excel;
using Version = System.Version;
-namespace Greenshot.Plugin.Office.OfficeExport
+namespace GreenshotOfficePlugin.OfficeExport
{
///
/// Excel exporter
@@ -53,12 +53,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no excel running
return null;
}
-
if (excelApplication?.ComObject != null)
{
InitializeVariables(excelApplication);
}
-
return excelApplication;
}
@@ -73,7 +71,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
excelApplication = DisposableCom.Create(new Application());
}
-
InitializeVariables(excelApplication);
return excelApplication;
}
@@ -111,11 +108,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
-
if (!Version.TryParse(excelApplication.ComObject.Version, out _excelVersion))
{
LOG.Warn("Assuming Excel version 1997.");
- _excelVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0);
+ _excelVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0);
}
}
@@ -136,7 +132,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var workbooks = DisposableCom.Create(excelApplication.ComObject.Workbooks);
for (int i = 1; i <= workbooks.ComObject.Count; i++)
{
- using var workbook = DisposableCom.Create((_Workbook) workbooks.ComObject[i]);
+ using var workbook = DisposableCom.Create((_Workbook)workbooks.ComObject[i]);
if ((workbook != null) && workbook.ComObject.Name.StartsWith(workbookName))
{
InsertIntoExistingWorkbook(workbook, tmpFile, imageSize);
@@ -177,7 +173,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
shape.ComObject.ScaleWidth(1, MsoTriState.msoTrue, MsoScaleFrom.msoScaleFromTopLeft);
workbook.ComObject.Activate();
using var application = DisposableCom.Create(workbook.ComObject.Application);
- User32Api.SetForegroundWindow((IntPtr) application.ComObject.Hwnd);
+ User32.SetForegroundWindow((IntPtr) application.ComObject.Hwnd);
}
///
@@ -195,8 +191,9 @@ namespace Greenshot.Plugin.Office.OfficeExport
excelApplication.ComObject.Visible = true;
using var workbooks = DisposableCom.Create(excelApplication.ComObject.Workbooks);
- using var workbook = DisposableCom.Create((_Workbook) workbooks.ComObject.Add());
+ using var workbook = DisposableCom.Create((_Workbook)workbooks.ComObject.Add());
InsertIntoExistingWorkbook(workbook, tmpFile, imageSize);
}
}
+
}
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs b/GreenshotOfficePlugin/OfficeExport/OneNoteExporter.cs
similarity index 89%
rename from src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs
rename to GreenshotOfficePlugin/OfficeExport/OneNoteExporter.cs
index 645623780..611594334 100644
--- a/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs
+++ b/GreenshotOfficePlugin/OfficeExport/OneNoteExporter.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,34 +15,30 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Xml;
-using Greenshot.Base.Core;
-using Greenshot.Base.Core.Enums;
-using Greenshot.Base.Interfaces;
-using Greenshot.Base.Interfaces.Plugin;
-using Greenshot.Plugin.Office.Com;
-using Greenshot.Plugin.Office.OfficeExport.Entities;
+using GreenshotOfficePlugin.Com;
+using GreenshotOfficePlugin.OfficeExport.Entities;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
using Microsoft.Office.Interop.OneNote;
-namespace Greenshot.Plugin.Office.OfficeExport
+namespace GreenshotOfficePlugin.OfficeExport
{
///
/// OneNote exporter
- /// More details about OneNote: https://msdn.microsoft.com/en-us/magazine/ff796230.aspx
+ /// More details about OneNote: http://msdn.microsoft.com/en-us/magazine/ff796230.aspx
///
public class OneNoteExporter
{
private const string XmlImageContent = "{0}";
-
- private const string XmlOutline =
- "{0}";
-
+ private const string XmlOutline = "{0}";
private const string OnenoteNamespace2010 = "http://schemas.microsoft.com/office/onenote/2010/onenote";
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OneNoteExporter));
@@ -97,7 +93,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var pngStream = new MemoryStream();
var pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
- ImageIO.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings);
+ ImageOutput.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings);
var base64String = Convert.ToBase64String(pngStream.GetBuffer());
var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Image.Width, surfaceToUpload.Image.Height);
var pageChangesXml = string.Format(XmlOutline, imageXmlStr, page.Id, OnenoteNamespace2010, page.Name);
@@ -111,7 +107,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Unable to navigate to the target page", ex);
}
-
return true;
}
@@ -131,7 +126,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no OneNote running
return null;
}
-
return oneNoteApplication;
}
@@ -146,7 +140,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
oneNoteApplication = DisposableCom.Create(new Application());
}
-
return oneNoteApplication;
}
@@ -190,7 +183,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
};
}
}
-
if ("one:Section".Equals(xmlReader.Name))
{
string id = xmlReader.GetAttribute("ID");
@@ -204,7 +196,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
};
}
}
-
if ("one:Page".Equals(xmlReader.Name))
{
// Skip deleted items
@@ -223,7 +214,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
-
page.IsCurrentlyViewed = "true".Equals(xmlReader.GetAttribute("isCurrentlyViewed"));
pages.Add(page);
}
@@ -241,26 +231,22 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
catch (COMException cEx)
{
- if (cEx.ErrorCode == unchecked((int) 0x8002801D))
+ if (cEx.ErrorCode == unchecked((int)0x8002801D))
{
- LOG.Warn(
- "Wrong registry keys, to solve this remove the OneNote key as described here: https://microsoftmercenary.com/wp/outlook-excel-interop-calls-breaking-solved/");
+ LOG.Warn("Wrong registry keys, to solve this remove the OneNote key as described here: http://microsoftmercenary.com/wp/outlook-excel-interop-calls-breaking-solved/");
}
-
LOG.Warn("Problem retrieving onenote destinations, ignoring: ", cEx);
}
catch (Exception ex)
{
LOG.Warn("Problem retrieving onenote destinations, ignoring: ", ex);
}
-
pages.Sort((page1, page2) =>
{
if (page1.IsCurrentlyViewed || page2.IsCurrentlyViewed)
{
return page2.IsCurrentlyViewed.CompareTo(page1.IsCurrentlyViewed);
}
-
return string.Compare(page1.DisplayName, page2.DisplayName, StringComparison.Ordinal);
});
return pages;
@@ -278,7 +264,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return null;
}
-
// ReSharper disable once RedundantAssignment
string unfiledNotesPath = "";
oneNoteApplication.ComObject.GetSpecialLocation(specialLocation, out unfiledNotesPath);
@@ -300,7 +285,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
-
string id = xmlReader.GetAttribute("ID");
string path = xmlReader.GetAttribute("path");
if (unfiledNotesPath.Equals(path))
@@ -317,7 +301,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
-
return null;
}
}
diff --git a/src/Greenshot.Plugin.Office/OfficeExport/OutlookEmailExporter.cs b/GreenshotOfficePlugin/OfficeExport/OutlookEmailExporter.cs
similarity index 91%
rename from src/Greenshot.Plugin.Office/OfficeExport/OutlookEmailExporter.cs
rename to GreenshotOfficePlugin/OfficeExport/OutlookEmailExporter.cs
index 72724c40b..a0d0f54e0 100644
--- a/src/Greenshot.Plugin.Office/OfficeExport/OutlookEmailExporter.cs
+++ b/GreenshotOfficePlugin/OfficeExport/OutlookEmailExporter.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,15 +15,15 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
-using Greenshot.Base.IniFile;
-using Greenshot.Plugin.Office.Com;
-using Greenshot.Plugin.Office.OfficeInterop;
+using GreenshotOfficePlugin.Com;
+using GreenshotOfficePlugin.OfficeInterop;
+using GreenshotPlugin.IniFile;
using Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Interop.Word;
using Microsoft.Win32;
@@ -32,7 +32,7 @@ using Application = Microsoft.Office.Interop.Outlook.Application;
using Exception = System.Exception;
using Version = System.Version;
-namespace Greenshot.Plugin.Office.OfficeExport
+namespace GreenshotOfficePlugin.OfficeExport
{
///
/// Outlook exporter has all the functionality to export to outlook
@@ -47,9 +47,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
private const string ProfilesKey = @"Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\";
private const string AccountKey = "9375CFF0413111d3B88A00104B2A6676";
private const string NewSignatureValue = "New Signature";
-
private const string DefaultProfileValue = "DefaultProfile";
-
// Schema definitions for the MAPI properties, see: http://msdn.microsoft.com/en-us/library/aa454438.aspx and: http://msdn.microsoft.com/en-us/library/bb446117.aspx
private const string AttachmentContentId = @"http://schemas.microsoft.com/mapi/proptag/0x3712001E";
@@ -75,10 +73,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
// The activeexplorer inline response only works with >= 2013, Microsoft Outlook 15.0 Object Library
- if (_outlookVersion.Major >= (int) OfficeVersions.Office2013)
+ if (_outlookVersion.Major >= (int)OfficeVersions.Office2013)
{
// Check inline "panel" for Outlook 2013
- using var activeExplorer = DisposableCom.Create((_Explorer) outlookApplication.ComObject.ActiveExplorer());
+ using var activeExplorer = DisposableCom.Create((_Explorer)outlookApplication.ComObject.ActiveExplorer());
// Only if we have one and if the capture is the one we selected
if ((activeExplorer != null) && activeExplorer.ComObject.Caption.StartsWith(inspectorCaption))
{
@@ -92,17 +90,15 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return ExportToInspector(null, activeExplorer, mailItem.Class, mailItem, tmpFile, attachmentName);
}
-
break;
case AppointmentItem appointmentItem:
- if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
+ if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
{
if (!string.IsNullOrEmpty(appointmentItem.Organizer) && appointmentItem.Organizer.Equals(_currentUser))
{
return ExportToInspector(null, activeExplorer, appointmentItem.Class, null, tmpFile, attachmentName);
}
}
-
break;
}
}
@@ -114,11 +110,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return false;
}
-
LOG.DebugFormat("Got {0} inspectors to check", inspectors.ComObject.Count);
for (int i = 1; i <= inspectors.ComObject.Count; i++)
{
- using var inspector = DisposableCom.Create((_Inspector) inspectors.ComObject[i]);
+ using var inspector = DisposableCom.Create((_Inspector)inspectors.ComObject[i]);
string currentCaption = inspector.ComObject.Caption;
if (!currentCaption.StartsWith(inspectorCaption))
{
@@ -135,7 +130,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
-
try
{
return ExportToInspector(inspector, null, mailItem.Class, mailItem, tmpFile, attachmentName);
@@ -144,10 +138,9 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Error($"Export to {currentCaption} failed.", exExport);
}
-
break;
case AppointmentItem appointmentItem:
- if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
+ if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
{
if (!string.IsNullOrEmpty(appointmentItem.Organizer) && !appointmentItem.Organizer.Equals(_currentUser))
{
@@ -160,7 +153,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
// skip, can't export to olAppointment
continue;
}
-
try
{
return ExportToInspector(inspector, null, appointmentItem.Class, null, tmpFile, attachmentName);
@@ -169,7 +161,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Error($"Export to {currentCaption} failed.", exExport);
}
-
break;
default:
continue;
@@ -177,7 +168,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
-
return false;
}
@@ -191,8 +181,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
///
///
///
- private bool ExportToInspector(IDisposableCom<_Inspector> inspector, IDisposableCom<_Explorer> explorer, OlObjectClass itemClass, MailItem mailItem, string tmpFile,
- string attachmentName)
+ private bool ExportToInspector(IDisposableCom<_Inspector> inspector, IDisposableCom<_Explorer> explorer, OlObjectClass itemClass, MailItem mailItem, string tmpFile, string attachmentName)
{
bool isMail = OlObjectClass.olMail.Equals(itemClass);
bool isAppointment = OlObjectClass.olAppointment.Equals(itemClass);
@@ -201,7 +190,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.Warn("Item is no mail or appointment.");
return false;
}
-
try
{
// Make sure the inspector is activated, only this way the word editor is active!
@@ -212,27 +200,25 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
isTextFormat = OlBodyFormat.olFormatPlain.Equals(mailItem.BodyFormat);
}
-
if (isAppointment || !isTextFormat)
{
// Check for wordmail, if so use the wordexporter
- // https://msdn.microsoft.com/en-us/library/dd492012%28v=office.12%29.aspx
+ // http://msdn.microsoft.com/en-us/library/dd492012%28v=office.12%29.aspx
// Earlier versions of Outlook also supported an Inspector.HTMLEditor object property, but since Internet Explorer is no longer the rendering engine for HTML messages and posts, HTMLEditor is no longer supported.
IDisposableCom<_Document> wordDocument = null;
- if ((explorer != null) && (_outlookVersion.Major >= (int) OfficeVersions.Office2013))
+ if ((explorer != null) && (_outlookVersion.Major >= (int)OfficeVersions.Office2013))
{
// TODO: Needs to have the Microsoft Outlook 15.0 Object Library installed
- wordDocument = DisposableCom.Create((_Document) explorer.ComObject.ActiveInlineResponseWordEditor);
+ wordDocument = DisposableCom.Create((_Document)explorer.ComObject.ActiveInlineResponseWordEditor);
}
else if (inspector != null)
{
if (inspector.ComObject.IsWordMail() && (inspector.ComObject.EditorType == OlEditorType.olEditorWord))
{
- var tmpWordDocument = (_Document) inspector.ComObject.WordEditor;
+ var tmpWordDocument = (_Document)inspector.ComObject.WordEditor;
wordDocument = DisposableCom.Create(tmpWordDocument);
}
}
-
if (wordDocument != null)
{
using (wordDocument)
@@ -262,7 +248,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.Info("Trying export for outlook < 2007.");
}
}
-
// Only use mailitem as it should be filled!!
if (mailItem != null)
{
@@ -270,7 +255,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
string contentId;
- if (_outlookVersion.Major >= (int) OfficeVersions.Office2007)
+ if (_outlookVersion.Major >= (int)OfficeVersions.Office2007)
{
contentId = Guid.NewGuid().ToString();
}
@@ -298,7 +283,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
var selection = document2.selection;
if (selection != null)
{
- var range = (IHTMLTxtRange) selection.createRange();
+ var range = (IHTMLTxtRange)selection.createRange();
if (range != null)
{
// First paste, than attach (otherwise the range is wrong!)
@@ -330,7 +315,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Create the attachment (if inlined the attachment isn't visible as attachment!)
using var attachments = DisposableCom.Create(mailItem.Attachments);
using var attachment = DisposableCom.Create(attachments.ComObject.Add(tmpFile, OlAttachmentType.olByValue, inlinePossible ? 0 : 1, attachmentName));
- if (_outlookVersion.Major >= (int) OfficeVersions.Office2007)
+ if (_outlookVersion.Major >= (int)OfficeVersions.Office2007)
{
// Add the content id to the attachment, this only works for Outlook >= 2007
try
@@ -356,11 +341,9 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
caption = explorer.ComObject.Caption;
}
-
LOG.Warn($"Problem while trying to add attachment to Item '{caption}'", ex);
return false;
}
-
try
{
if (inspector != null)
@@ -377,7 +360,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.Warn("Problem activating inspector/explorer: ", ex);
return false;
}
-
LOG.Debug("Finished!");
return true;
}
@@ -394,32 +376,27 @@ namespace Greenshot.Plugin.Office.OfficeExport
///
///
///
- private void ExportToNewEmail(IDisposableCom outlookApplication, EmailFormat format, string tmpFile, string subject, string attachmentName, string to,
- string cc, string bcc, string url)
+ private void ExportToNewEmail(IDisposableCom outlookApplication, EmailFormat format, string tmpFile, string subject, string attachmentName, string to, string cc, string bcc, string url)
{
- using var newItem = DisposableCom.Create((MailItem) outlookApplication.ComObject.CreateItem(OlItemType.olMailItem));
+ using var newItem = DisposableCom.Create((MailItem)outlookApplication.ComObject.CreateItem(OlItemType.olMailItem));
if (newItem == null)
{
return;
}
-
var newMail = newItem.ComObject;
newMail.Subject = subject;
if (!string.IsNullOrEmpty(to))
{
newMail.To = to;
}
-
if (!string.IsNullOrEmpty(cc))
{
newMail.CC = cc;
}
-
if (!string.IsNullOrEmpty(bcc))
{
newMail.BCC = bcc;
}
-
newMail.BodyFormat = OlBodyFormat.olFormatHTML;
string bodyString = null;
// Read the default signature, if nothing found use empty email
@@ -431,7 +408,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Error("Problem reading signature!", e);
}
-
switch (format)
{
case EmailFormat.Text:
@@ -445,11 +421,9 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
bodyString = "";
}
-
newMail.Body = bodyString;
}
}
-
break;
default:
string contentId = Path.GetFileName(tmpFile);
@@ -458,7 +432,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
using var attachment = DisposableCom.Create(attachments.ComObject.Add(tmpFile, OlAttachmentType.olByValue, 0, attachmentName));
// add content ID to the attachment
- if (_outlookVersion.Major >= (int) OfficeVersions.Office2007)
+ if (_outlookVersion.Major >= (int)OfficeVersions.Office2007)
{
try
{
@@ -482,7 +456,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
href = $"";
hrefEnd = "";
}
-
string htmlImgEmbedded = $" {href}{hrefEnd} ";
string fallbackBody = $"{htmlImgEmbedded}";
if (bodyString == null)
@@ -509,15 +482,13 @@ namespace Greenshot.Plugin.Office.OfficeExport
bodyString = fallbackBody;
}
}
-
newMail.HTMLBody = bodyString;
break;
}
-
// So not save, otherwise the email is always stored in Draft folder.. (newMail.Save();)
newMail.Display(false);
- using var inspector = DisposableCom.Create((_Inspector) newMail.GetInspector);
+ using var inspector = DisposableCom.Create((_Inspector)newMail.GetInspector);
if (inspector != null)
{
try
@@ -557,14 +528,12 @@ namespace Greenshot.Plugin.Office.OfficeExport
exported = true;
}
}
-
return exported;
}
catch (Exception e)
{
LOG.Error("Error while creating an outlook mail item: ", e);
}
-
return exported;
}
@@ -579,7 +548,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
outlookApplication = DisposableCom.Create(new Application());
}
-
InitializeVariables(outlookApplication);
return outlookApplication;
}
@@ -600,12 +568,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no outlook running
return null;
}
-
if ((outlookApplication != null) && (outlookApplication.ComObject != null))
{
InitializeVariables(outlookApplication);
}
-
return outlookApplication;
}
@@ -621,8 +587,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return null;
}
-
- string defaultProfile = (string) profilesKey.GetValue(DefaultProfileValue);
+ string defaultProfile = (string)profilesKey.GetValue(DefaultProfileValue);
LOG.DebugFormat("defaultProfile={0}", defaultProfile);
using var profileKey = profilesKey.OpenSubKey(defaultProfile + @"\" + AccountKey, false);
if (profileKey != null)
@@ -634,21 +599,19 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var numberKey = profileKey.OpenSubKey(number, false);
if (numberKey != null)
{
- byte[] val = (byte[]) numberKey.GetValue(NewSignatureValue);
+ byte[] val = (byte[])numberKey.GetValue(NewSignatureValue);
if (val == null)
{
continue;
}
-
string signatureName = "";
foreach (byte b in val)
{
if (b != 0)
{
- signatureName += (char) b;
+ signatureName += (char)b;
}
}
-
LOG.DebugFormat("Found email signature: {0}", signatureName);
var extension = format switch
{
@@ -665,7 +628,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
-
return null;
}
@@ -680,15 +642,13 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
-
if (!Version.TryParse(outlookApplication.ComObject.Version, out _outlookVersion))
{
LOG.Warn("Assuming outlook version 1997.");
- _outlookVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0);
+ _outlookVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0);
}
-
// Preventing retrieval of currentUser if Outlook is older than 2007
- if (_outlookVersion.Major >= (int) OfficeVersions.Office2007)
+ if (_outlookVersion.Major >= (int)OfficeVersions.Office2007)
{
try
{
@@ -697,7 +657,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var currentUser = DisposableCom.Create(mapiNamespace.ComObject.CurrentUser);
_currentUser = currentUser.ComObject.Name;
}
-
LOG.InfoFormat("Current user: {0}", _currentUser);
}
catch (Exception exNs)
@@ -723,7 +682,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
// The activeexplorer inline response only works with >= 2013, Microsoft Outlook 15.0 Object Library
- if (_outlookVersion.Major >= (int) OfficeVersions.Office2013)
+ if (_outlookVersion.Major >= (int)OfficeVersions.Office2013)
{
// Check inline "panel" for Outlook 2013
using var activeExplorer = DisposableCom.Create(outlookApplication.ComObject.ActiveExplorer());
@@ -742,17 +701,15 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
inspectorCaptions.Add(caption, mailItem.Class);
}
-
break;
case AppointmentItem appointmentItem:
- if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
+ if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
{
if (!string.IsNullOrEmpty(appointmentItem.Organizer) && appointmentItem.Organizer.Equals(_currentUser))
{
inspectorCaptions.Add(caption, appointmentItem.Class);
}
}
-
break;
}
}
@@ -783,11 +740,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
-
inspectorCaptions.Add(caption, mailItem.Class);
break;
case AppointmentItem appointmentItem:
- if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
+ if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings)
{
if (!string.IsNullOrEmpty(appointmentItem.Organizer) && !appointmentItem.Organizer.Equals(_currentUser))
{
@@ -800,7 +756,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
// skip, can't export to olAppointment
continue;
}
-
inspectorCaptions.Add(caption, appointmentItem.Class);
break;
default:
@@ -814,7 +769,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Problem retrieving word destinations, ignoring: ", ex);
}
-
return inspectorCaptions;
}
}
diff --git a/src/Greenshot.Plugin.Office/OfficeExport/PowerpointExporter.cs b/GreenshotOfficePlugin/OfficeExport/PowerpointExporter.cs
similarity index 93%
rename from src/Greenshot.Plugin.Office/OfficeExport/PowerpointExporter.cs
rename to GreenshotOfficePlugin/OfficeExport/PowerpointExporter.cs
index 21862f38d..237fea1f9 100644
--- a/src/Greenshot.Plugin.Office/OfficeExport/PowerpointExporter.cs
+++ b/GreenshotOfficePlugin/OfficeExport/PowerpointExporter.cs
@@ -1,33 +1,33 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
-//
-// For more information see: https://getgreenshot.org/
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+//
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
-//
+//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version.
-//
+//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
using System;
using System.Collections.Generic;
using System.Drawing;
-using Greenshot.Base.IniFile;
-using Greenshot.Plugin.Office.Com;
-using Greenshot.Plugin.Office.OfficeInterop;
+using GreenshotOfficePlugin.Com;
+using GreenshotOfficePlugin.OfficeInterop;
+using GreenshotPlugin.IniFile;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
using Shape = Microsoft.Office.Interop.PowerPoint.Shape;
-namespace Greenshot.Plugin.Office.OfficeExport
+namespace GreenshotOfficePlugin.OfficeExport
{
///
/// Export logic for powerpoint
@@ -60,7 +60,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
left = pageSetup.ComObject.SlideWidth / 2 - imageSize.Width / 2f;
top = pageSetup.ComObject.SlideHeight / 2 - imageSize.Height / 2f;
}
-
float width = imageSize.Width;
float height = imageSize.Height;
IDisposableCom shapeForCaption = null;
@@ -87,7 +86,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
shapeForLocation.ComObject.Left = left;
}
-
shapeForLocation.ComObject.Width = imageSize.Width;
if (height > shapeForLocation.ComObject.Height)
@@ -100,7 +98,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
top = shapeForLocation.ComObject.Top + shapeForLocation.ComObject.Height / 2 - imageSize.Height / 2f;
}
-
shapeForLocation.ComObject.Height = imageSize.Height;
}
catch (Exception e)
@@ -109,7 +106,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var slides = DisposableCom.Create(presentation.ComObject.Slides);
slide = DisposableCom.Create(slides.ComObject.Add(slides.ComObject.Count + 1, PpSlideLayout.ppLayoutBlank));
}
-
using (var shapes = DisposableCom.Create(slide.ComObject.Shapes))
{
using var shape = DisposableCom.Create(shapes.ComObject.AddPicture(tmpFile, MsoTriState.msoFalse, MsoTriState.msoTrue, 0, 0, width, height));
@@ -121,24 +117,20 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
shape.ComObject.LockAspectRatio = MsoTriState.msoFalse;
}
-
shape.ComObject.ScaleHeight(1, MsoTriState.msoTrue, MsoScaleFrom.msoScaleFromMiddle);
shape.ComObject.ScaleWidth(1, MsoTriState.msoTrue, MsoScaleFrom.msoScaleFromMiddle);
if (hasScaledWidth)
{
shape.ComObject.Width = width;
}
-
if (hasScaledHeight)
{
shape.ComObject.Height = height;
}
-
shape.ComObject.Left = left;
shape.ComObject.Top = top;
shape.ComObject.AlternativeText = title;
}
-
if (shapeForCaption != null)
{
try
@@ -156,7 +148,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.Warn("Problem setting the title to a text-range", ex);
}
}
-
// Activate/Goto the slide
try
{
@@ -203,12 +194,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
-
if (!presentation.ComObject.Name.StartsWith(presentationName))
{
continue;
}
-
try
{
AddPictureToPresentation(presentation, tmpFile, imageSize, title);
@@ -220,7 +209,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
-
return false;
}
@@ -235,7 +223,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
powerPointApplication = DisposableCom.Create(new Application());
}
-
InitializeVariables(powerPointApplication);
return powerPointApplication;
}
@@ -256,12 +243,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no PowerPoint running
return null;
}
-
- if (powerPointApplication?.ComObject != null)
+ if (powerPointApplication.ComObject != null)
{
InitializeVariables(powerPointApplication);
}
-
return powerPointApplication;
}
@@ -286,12 +271,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
-
if (presentation.ComObject.ReadOnly == MsoTriState.msoTrue)
{
continue;
}
-
if (IsAfter2003())
{
if (presentation.ComObject.Final)
@@ -299,7 +282,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
continue;
}
}
-
yield return presentation.ComObject.Name;
}
}
@@ -314,11 +296,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
-
if (!Version.TryParse(powerpointApplication.ComObject.Version, out _powerpointVersion))
{
LOG.Warn("Assuming Powerpoint version 1997.");
- _powerpointVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0);
+ _powerpointVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0);
}
}
@@ -351,13 +332,13 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
-
return isPictureAdded;
}
private bool IsAfter2003()
{
- return _powerpointVersion.Major > (int) OfficeVersions.Office2003;
+ return _powerpointVersion.Major > (int)OfficeVersions.Office2003;
}
}
+
}
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Office/OfficeExport/WordExporter.cs b/GreenshotOfficePlugin/OfficeExport/WordExporter.cs
similarity index 93%
rename from src/Greenshot.Plugin.Office/OfficeExport/WordExporter.cs
rename to GreenshotOfficePlugin/OfficeExport/WordExporter.cs
index 8100bf650..0132d86b0 100644
--- a/src/Greenshot.Plugin.Office/OfficeExport/WordExporter.cs
+++ b/GreenshotOfficePlugin/OfficeExport/WordExporter.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub: https://github.com/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,18 +15,18 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
using System;
using System.Collections.Generic;
-using Greenshot.Base.IniFile;
-using Greenshot.Plugin.Office.Com;
-using Greenshot.Plugin.Office.OfficeInterop;
+using GreenshotOfficePlugin.Com;
+using GreenshotOfficePlugin.OfficeInterop;
+using GreenshotPlugin.IniFile;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.Word;
using Version = System.Version;
-namespace Greenshot.Plugin.Office.OfficeExport
+namespace GreenshotOfficePlugin.OfficeExport
{
///
/// This makes it possible to export to word
@@ -53,7 +53,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
shape.ComObject.LockAspectRatio = MsoTriState.msoTrue;
}
-
selection.ComObject.InsertAfter("\r\n");
selection.ComObject.MoveDown(WdUnits.wdLine, 1, Type.Missing);
return shape;
@@ -70,7 +69,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
wordApplication = DisposableCom.Create(new Application());
}
-
InitializeVariables(wordApplication);
return wordApplication;
}
@@ -91,12 +89,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
// Ignore, probably no word running
return null;
}
-
if ((wordApplication != null) && (wordApplication.ComObject != null))
{
InitializeVariables(wordApplication);
}
-
return wordApplication;
}
@@ -120,7 +116,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
continue;
}
-
if (IsAfter2003())
{
if (document.ComObject.Final)
@@ -144,11 +139,10 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
-
if (!Version.TryParse(wordApplication.ComObject.Version, out _wordVersion))
{
LOG.Warn("Assuming Word version 1997.");
- _wordVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0);
+ _wordVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0);
}
}
@@ -170,7 +164,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
using var documents = DisposableCom.Create(wordApplication.ComObject.Documents);
for (int i = 1; i <= documents.ComObject.Count; i++)
{
- using var wordDocument = DisposableCom.Create((_Document) documents.ComObject[i]);
+ using var wordDocument = DisposableCom.Create((_Document)documents.ComObject[i]);
using var activeWindow = DisposableCom.Create(wordDocument.ComObject.ActiveWindow);
if (activeWindow.ComObject.Caption.StartsWith(wordCaption))
{
@@ -178,7 +172,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
-
return false;
}
@@ -191,8 +184,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
/// string
/// string with the tooltip of the image
/// bool
- internal bool InsertIntoExistingDocument(IDisposableCom wordApplication, IDisposableCom<_Document> wordDocument, string tmpFile, string address,
- string tooltip)
+ internal bool InsertIntoExistingDocument(IDisposableCom wordApplication, IDisposableCom<_Document> wordDocument, string tmpFile, string address, string tooltip)
{
// Bug #1517: image will be inserted into that document, where the focus was last. It will not inserted into the chosen one.
// Solution: Make sure the selected document is active, otherwise the insert will be made in a different document!
@@ -212,7 +204,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.InfoFormat("No selection to insert {0} into found.", tmpFile);
return false;
}
-
// Add Picture
using (var shape = AddPictureToSelection(selection, tmpFile))
{
@@ -223,7 +214,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
screentip = tooltip;
}
-
try
{
using var hyperlinks = DisposableCom.Create(wordDocument.ComObject.Hyperlinks);
@@ -235,7 +225,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
-
try
{
// When called for Outlook, the follow error is created: This object model command is not available in e-mail
@@ -256,7 +245,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
LOG.WarnFormat("Couldn't set zoom to 100, error: {0}", e.Message);
}
}
-
try
{
wordApplication.ComObject.Activate();
@@ -266,7 +254,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Error activating word application", ex);
}
-
try
{
wordDocument.ComObject.Activate();
@@ -276,7 +263,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Error activating word document", ex);
}
-
return true;
}
@@ -293,7 +279,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
return;
}
-
wordApplication.ComObject.Visible = true;
wordApplication.ComObject.Activate();
// Create new Document
@@ -314,7 +299,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
screentip = tooltip;
}
-
try
{
using var hyperlinks = DisposableCom.Create(wordDocument.ComObject.Hyperlinks);
@@ -329,7 +313,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
}
}
}
-
try
{
wordDocument.ComObject.Activate();
@@ -339,7 +322,6 @@ namespace Greenshot.Plugin.Office.OfficeExport
{
LOG.Warn("Error activating word document", ex);
}
-
try
{
using var activeWindow = DisposableCom.Create(wordDocument.ComObject.ActiveWindow);
@@ -358,7 +340,7 @@ namespace Greenshot.Plugin.Office.OfficeExport
///
private bool IsAfter2003()
{
- return _wordVersion.Major > (int) OfficeVersions.Office2003;
+ return _wordVersion.Major > (int)OfficeVersions.Office2003;
}
}
}
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Office/OfficeInterop/EmailFormat.cs b/GreenshotOfficePlugin/OfficeInterop/EmailFormat.cs
similarity index 66%
rename from src/Greenshot.Plugin.Office/OfficeInterop/EmailFormat.cs
rename to GreenshotOfficePlugin/OfficeInterop/EmailFormat.cs
index d01a289f4..98969057f 100644
--- a/src/Greenshot.Plugin.Office/OfficeInterop/EmailFormat.cs
+++ b/GreenshotOfficePlugin/OfficeInterop/EmailFormat.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,23 +15,22 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
-namespace Greenshot.Plugin.Office.OfficeInterop
+namespace GreenshotOfficePlugin.OfficeInterop
{
- ///
- /// Specifies which EmailFormat the email needs to use
- ///
- public enum EmailFormat
- {
+ ///
+ /// Specifies which EmailFormat the email needs to use
+ ///
+ public enum EmailFormat
+ {
///
/// Use the plain text format
///
- Text,
-
+ Text,
///
/// Use HTML format
///
- Html
- }
+ Html
+ }
}
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Office/OfficeInterop/OfficeVersions.cs b/GreenshotOfficePlugin/OfficeInterop/OfficeVersions.cs
similarity index 64%
rename from src/Greenshot.Plugin.Office/OfficeInterop/OfficeVersions.cs
rename to GreenshotOfficePlugin/OfficeInterop/OfficeVersions.cs
index e39d000ff..6d8a992ba 100644
--- a/src/Greenshot.Plugin.Office/OfficeInterop/OfficeVersions.cs
+++ b/GreenshotOfficePlugin/OfficeInterop/OfficeVersions.cs
@@ -1,7 +1,7 @@
// Greenshot - a free and open source screenshot tool
-// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
//
-// For more information see: https://getgreenshot.org/
+// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
@@ -15,48 +15,50 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
-namespace Greenshot.Plugin.Office.OfficeInterop
+namespace GreenshotOfficePlugin.OfficeInterop
{
- ///
- /// A mapping between the version and a usable name
- ///
- public enum OfficeVersions
- {
+ ///
+ /// A mapping between the version and a usable name
+ ///
+ public enum OfficeVersions
+ {
///
/// Office 97
///
- Office97 = 8,
-
+ Office97 = 8,
+ ///
+ /// Office 2000
+ ///
+ Office2000 = 9,
+ ///
+ /// Office 2002
+ ///
+ Office2002 = 10,
///
/// Office 2003
///
- Office2003 = 11,
-
+ Office2003 = 11,
///
/// Office 2007
///
- Office2007 = 12,
-
+ Office2007 = 12,
///
/// Office 2010
///
- Office2010 = 14,
-
+ Office2010 = 14,
///
/// Office 2013
///
- Office2013 = 15,
-
+ Office2013 = 15,
///
/// Office 2016
///
Office2016 = 16,
-
///
/// Office 2019
///
Office2019 = 17
- }
+ }
}
\ No newline at end of file
diff --git a/GreenshotOfficePlugin/OfficePlugin.cs b/GreenshotOfficePlugin/OfficePlugin.cs
new file mode 100644
index 000000000..4e01b38fa
--- /dev/null
+++ b/GreenshotOfficePlugin/OfficePlugin.cs
@@ -0,0 +1,115 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using GreenshotOfficePlugin.Destinations;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotOfficePlugin {
+ ///
+ /// This is the OfficePlugin base code
+ ///
+ [Plugin("Office", false)]
+ public class OfficePlugin : IGreenshotPlugin
+ {
+ private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OfficePlugin));
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected void Dispose(bool disposing) {
+ // Do nothing
+ }
+
+ private IEnumerable Destinations() {
+ IDestination destination;
+ try {
+ destination = new ExcelDestination();
+ } catch {
+ destination = null;
+ }
+ if (destination != null) {
+ yield return destination;
+ }
+
+ try {
+ destination = new PowerpointDestination();
+ } catch {
+ destination = null;
+ }
+ if (destination != null) {
+ yield return destination;
+ }
+
+ try {
+ destination = new WordDestination();
+ } catch {
+ destination = null;
+ }
+ if (destination != null) {
+ yield return destination;
+ }
+
+ try {
+ destination = new OutlookDestination();
+ } catch {
+ destination = null;
+ }
+ if (destination != null) {
+ yield return destination;
+ }
+
+ try {
+ destination = new OneNoteDestination();
+ } catch {
+ destination = null;
+ }
+ if (destination != null) {
+ yield return destination;
+ }
+ }
+
+
+ ///
+ /// Implementation of the IGreenshotPlugin.Initialize
+ ///
+ /// true if plugin is initialized, false if not (doesn't show)
+ public bool Initialize() {
+ SimpleServiceProvider.Current.AddService(Destinations());
+ return true;
+ }
+
+ public void Shutdown() {
+ LOG.Debug("Office Plugin shutdown.");
+ }
+
+ ///
+ /// Implementation of the IPlugin.Configure
+ ///
+ public void Configure() {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs b/GreenshotOfficePlugin/Properties/AssemblyInfo.cs
similarity index 79%
rename from src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs
rename to GreenshotOfficePlugin/Properties/AssemblyInfo.cs
index 6b14a40f5..50c27e0ec 100644
--- a/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs
+++ b/GreenshotOfficePlugin/Properties/AssemblyInfo.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,19 +16,17 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
using System.Reflection;
using System.Runtime.InteropServices;
-using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyDescription("A plugin to export images to Office applications")]
-[assembly: AssemblyPluginIdentifier("Office Plugin")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
-[assembly: ComVisible(false)]
\ No newline at end of file
+[assembly: ComVisible(false)]
diff --git a/GreenshotPhotobucketPlugin/Forms/PhotobucketForm.cs b/GreenshotPhotobucketPlugin/Forms/PhotobucketForm.cs
new file mode 100644
index 000000000..21c90f7d1
--- /dev/null
+++ b/GreenshotPhotobucketPlugin/Forms/PhotobucketForm.cs
@@ -0,0 +1,28 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotPhotobucketPlugin {
+ ///
+ /// This class is needed for design-time resolving of the language files
+ ///
+ public class PhotobucketForm : GreenshotPlugin.Controls.GreenshotForm {
+ }
+}
diff --git a/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs b/GreenshotPhotobucketPlugin/Forms/SettingsForm.Designer.cs
similarity index 79%
rename from src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs
rename to GreenshotPhotobucketPlugin/Forms/SettingsForm.Designer.cs
index 1fff9f5c3..4cd900c68 100644
--- a/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs
+++ b/GreenshotPhotobucketPlugin/Forms/SettingsForm.Designer.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,12 +16,9 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
-
-using Greenshot.Base.Controls;
-
-namespace Greenshot.Plugin.Photobucket.Forms {
+namespace GreenshotPhotobucketPlugin {
partial class SettingsForm {
///
/// Designer variable used to keep track of non-visual components.
@@ -49,11 +46,11 @@ namespace Greenshot.Plugin.Photobucket.Forms {
///
private void InitializeComponent()
{
- this.buttonOK = new GreenshotButton();
- this.buttonCancel = new GreenshotButton();
- this.combobox_uploadimageformat = new GreenshotComboBox();
- this.label_upload_format = new GreenshotLabel();
- this.checkbox_usepagelink = new GreenshotCheckBox();
+ this.buttonOK = new GreenshotPlugin.Controls.GreenshotButton();
+ this.buttonCancel = new GreenshotPlugin.Controls.GreenshotButton();
+ this.combobox_uploadimageformat = new GreenshotPlugin.Controls.GreenshotComboBox();
+ this.label_upload_format = new GreenshotPlugin.Controls.GreenshotLabel();
+ this.checkbox_usepagelink = new GreenshotPlugin.Controls.GreenshotCheckBox();
this.SuspendLayout();
//
// buttonOK
@@ -84,7 +81,7 @@ namespace Greenshot.Plugin.Photobucket.Forms {
this.combobox_uploadimageformat.FormattingEnabled = true;
this.combobox_uploadimageformat.Location = new System.Drawing.Point(102, 11);
this.combobox_uploadimageformat.Name = "combobox_uploadimageformat";
- this.combobox_uploadimageformat.PropertyName = nameof(PhotobucketConfiguration.UploadFormat);
+ this.combobox_uploadimageformat.PropertyName = "UploadFormat";
this.combobox_uploadimageformat.SectionName = "Photobucket";
this.combobox_uploadimageformat.Size = new System.Drawing.Size(276, 21);
this.combobox_uploadimageformat.TabIndex = 1;
@@ -99,10 +96,11 @@ namespace Greenshot.Plugin.Photobucket.Forms {
//
// checkbox_usepagelink
//
+ this.checkbox_usepagelink.AutoSize = true;
this.checkbox_usepagelink.LanguageKey = "photobucket.use_page_link";
this.checkbox_usepagelink.Location = new System.Drawing.Point(15, 43);
this.checkbox_usepagelink.Name = "checkbox_usepagelink";
- this.checkbox_usepagelink.PropertyName = nameof(PhotobucketConfiguration.UsePageLink);
+ this.checkbox_usepagelink.PropertyName = "UsePageLink";
this.checkbox_usepagelink.SectionName = "Photobucket";
this.checkbox_usepagelink.Size = new System.Drawing.Size(251, 17);
this.checkbox_usepagelink.TabIndex = 2;
@@ -127,10 +125,10 @@ namespace Greenshot.Plugin.Photobucket.Forms {
this.PerformLayout();
}
- private GreenshotComboBox combobox_uploadimageformat;
- private GreenshotLabel label_upload_format;
- private GreenshotButton buttonCancel;
- private GreenshotButton buttonOK;
- private GreenshotCheckBox checkbox_usepagelink;
+ private GreenshotPlugin.Controls.GreenshotComboBox combobox_uploadimageformat;
+ private GreenshotPlugin.Controls.GreenshotLabel label_upload_format;
+ private GreenshotPlugin.Controls.GreenshotButton buttonCancel;
+ private GreenshotPlugin.Controls.GreenshotButton buttonOK;
+ private GreenshotPlugin.Controls.GreenshotCheckBox checkbox_usepagelink;
}
}
diff --git a/GreenshotPhotobucketPlugin/Forms/SettingsForm.cs b/GreenshotPhotobucketPlugin/Forms/SettingsForm.cs
new file mode 100644
index 000000000..056d696a9
--- /dev/null
+++ b/GreenshotPhotobucketPlugin/Forms/SettingsForm.cs
@@ -0,0 +1,37 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotPhotobucketPlugin {
+ ///
+ /// Description of PasswordRequestForm.
+ ///
+ public partial class SettingsForm : PhotobucketForm {
+ public SettingsForm()
+ {
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+ AcceptButton = buttonOK;
+ CancelButton = buttonCancel;
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.Credentials.template b/GreenshotPhotobucketPlugin/GreenshotPhotobucketPlugin.Credentials.template
similarity index 92%
rename from src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.Credentials.template
rename to GreenshotPhotobucketPlugin/GreenshotPhotobucketPlugin.Credentials.template
index 8895f5a73..20a033ea8 100644
--- a/src/Greenshot.Plugin.Photobucket/Greenshot.Plugin.Photobucket.Credentials.template
+++ b/GreenshotPhotobucketPlugin/GreenshotPhotobucketPlugin.Credentials.template
@@ -1,6 +1,6 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
@@ -19,7 +19,7 @@
* along with this program. If not, see .
*/
-namespace Greenshot.Plugin.Photobucket {
+namespace GreenshotPhotobucketPlugin {
///
/// This class is merely a placeholder for the file keeping the API key and secret for photobucket integration.
/// You can set your own values here
diff --git a/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj b/GreenshotPhotobucketPlugin/GreenshotPhotobucketPlugin.csproj
similarity index 52%
rename from src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj
rename to GreenshotPhotobucketPlugin/GreenshotPhotobucketPlugin.csproj
index d0151b7c0..bab11d46e 100644
--- a/src/Greenshot.Plugin.ExternalCommand/Greenshot.Plugin.ExternalCommand.csproj
+++ b/GreenshotPhotobucketPlugin/GreenshotPhotobucketPlugin.csproj
@@ -1,8 +1,10 @@
-
- none
- false
+
+
+ GreenshotPhotobucketPlugin
+ GreenshotPhotobucketPlugin
+
PreserveNewest
@@ -10,6 +12,6 @@
-
+
diff --git a/GreenshotPhotobucketPlugin/LanguageKeys.cs b/GreenshotPhotobucketPlugin/LanguageKeys.cs
new file mode 100644
index 000000000..5c077c4ad
--- /dev/null
+++ b/GreenshotPhotobucketPlugin/LanguageKeys.cs
@@ -0,0 +1,34 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotPhotobucketPlugin {
+ public enum LangKey {
+ upload_menu_item,
+ settings_title,
+ label_upload_format,
+ label_clear,
+ upload_success,
+ upload_failure,
+ communication_wait,
+ use_page_link,
+ configure
+ }
+}
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-cs-CZ.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-cs-CZ.xml
similarity index 98%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-cs-CZ.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-cs-CZ.xml
index 09f5b572e..437fe1072 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-cs-CZ.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-cs-CZ.xml
@@ -1,15 +1,15 @@
-
-
-
- Zrušit
- Komunikace s Photobucket. Prosím čekejte ...
- Nastavení Photobucket
- Formát obrázku
- OK
- Nastavení Photobucket
- Při nahrávání na Photobucket došlo k chybě:
- Nahrát na Photobucket
- Odeslání obrázku na bylo Photobucket úspěšné!
- Použijte odkaz na stránku namísto odkaz na obrázek do schránky
-
+
+
+
+ Zrušit
+ Komunikace s Photobucket. Prosím čekejte ...
+ Nastavení Photobucket
+ Formát obrázku
+ OK
+ Nastavení Photobucket
+ Při nahrávání na Photobucket došlo k chybě:
+ Nahrát na Photobucket
+ Odeslání obrázku na bylo Photobucket úspěšné!
+ Použijte odkaz na stránku namísto odkaz na obrázek do schránky
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-de-DE.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-de-DE.xml
similarity index 96%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-de-DE.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-de-DE.xml
index f498058b1..74f7f17f9 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-de-DE.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-de-DE.xml
@@ -1,35 +1,35 @@
-
-
-
-
- Zu Photobucket hochladen
-
-
- Photobucket Einstellungen
-
-
- OK
-
-
- Cancel
-
-
- Das Hochladen zu Photobucket war erfolgreich.
-
-
- Es gab einen Fehler beim Hochladen zu Photobucket:
-
-
- Bildformat
-
-
- Übermittle Daten zu Photobucket. Bitte warten...
-
-
- Benutze der Seite-URL statt Bild-URL im Zwischenablage
-
-
- Photobucket Einstellungen
-
-
+
+
+
+
+ Zu Photobucket hochladen
+
+
+ Photobucket Einstellungen
+
+
+ OK
+
+
+ Cancel
+
+
+ Das Hochladen zu Photobucket war erfolgreich.
+
+
+ Es gab einen Fehler beim Hochladen zu Photobucket:
+
+
+ Bildformat
+
+
+ Übermittle Daten zu Photobucket. Bitte warten...
+
+
+ Benutze der Seite-URL statt Bild-URL im Zwischenablage
+
+
+ Photobucket Einstellungen
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-en-US.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-en-US.xml
similarity index 90%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-en-US.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-en-US.xml
index 2cb07985c..cc2d3d241 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-en-US.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-en-US.xml
@@ -1,35 +1,35 @@
-
-
-
-
- Upload to Photobucket
-
-
- Photobucket settings
-
-
- OK
-
-
- Cancel
-
-
- Successfully uploaded image to Photobucket!
-
-
- An error occurred while uploading to Photobucket:
-
-
- Image format
-
-
- Communicating with Photobucket. Please wait...
-
-
- Use page link instead of image link on clipboard
-
-
- Configure Photobucket
-
-
+
+
+
+
+ Upload to Photobucket
+
+
+ Photobucket settings
+
+
+ OK
+
+
+ Cancel
+
+
+ Successfully uploaded image to Photobucket!
+
+
+ An error occured while uploading to Photobucket:
+
+
+ Image format
+
+
+ Communicating with Photobucket. Please wait...
+
+
+ Use page link instead of image link on clipboard
+
+
+ Configure Photobucket
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-fr_FR.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-fr_FR.xml
similarity index 96%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-fr_FR.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-fr_FR.xml
index 6d98bcd48..f2aa66b63 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-fr_FR.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-fr_FR.xml
@@ -1,29 +1,29 @@
-
-
-
-
- Téléverser vers Photobucket
-
-
- Paramètres Photobucket
-
-
- Utiliser proxy
-
-
- L'image a été téléversée vers Photobucket avec succès !
-
-
- Une erreur est survenue lors du téléversement vers Photobucket:
-
-
- Format d'image
-
-
- Téléversement vers Photobucket, veuillez patienter...
-
-
- Configurer Photobucket
-
-
+
+
+
+
+ Téléverser vers Photobucket
+
+
+ Paramètres Photobucket
+
+
+ Utiliser proxy
+
+
+ L'image a été téléversée vers Photobucket avec succès !
+
+
+ Une erreur est survenue lors du téléversement vers Photobucket:
+
+
+ Format d'image
+
+
+ Téléversement vers Photobucket, veuillez patienter...
+
+
+ Configurer Photobucket
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-id-ID.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-id-ID.xml
similarity index 98%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-id-ID.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-id-ID.xml
index 33541137d..b702ee68d 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-id-ID.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-id-ID.xml
@@ -1,15 +1,15 @@
-
-
-
- Batal
- Menyambungkan ke Photobucket. Tunggu sebentar...
- Konfigurasi Photobucket
- Format gambar
- Oke
- Pengaturan Photobucket
- Kesalahan terjadi ketika mengunggah ke Photobucket:
- Unggah ke Photobucket
- Berhasil mengunggah gambar ke Photobucket!
- Gunakan link laman daripada link gambar di papanklip
-
+
+
+
+ Batal
+ Menyambungkan ke Photobucket. Tunggu sebentar...
+ Konfigurasi Photobucket
+ Format gambar
+ Oke
+ Pengaturan Photobucket
+ Kesalahan terjadi ketika mengunggah ke Photobucket:
+ Unggah ke Photobucket
+ Berhasil mengunggah gambar ke Photobucket!
+ Gunakan link laman daripada link gambar di papanklip
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-it-IT.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-it-IT.xml
similarity index 69%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-it-IT.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-it-IT.xml
index 233153065..f0c1e80d2 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-it-IT.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-it-IT.xml
@@ -1,35 +1,35 @@
-
-
-
-
- Carica su Photobucket
-
-
- Impostazioni Photobucket
-
-
- OK
-
-
- Annulla
-
-
- Caricamento immagine su Photobucket completato!
-
-
- Si è verificato un errore durante il caricamento su Photobucket:
-
-
- Formato immagine
-
-
- Comunicazione con Photobucket...
-
-
- Negli Appunti usa il collegamento pagina invece di quello all'immagine
-
-
- Impostazioni Photobucket
-
-
-
+
+
+
+
+ Carica su Photobucket
+
+
+ Impostazioni Photobucket
+
+
+ OK
+
+
+ Annulla
+
+
+ Immagine caricata correttamente su Photobucket!
+
+
+ Si è verificato un errore durante il caricamento su Photobucket:
+
+
+ Formato immagine
+
+
+ Comunicazione con Photobucket. Attendere prego...
+
+
+ Usa il collegamento alla pagina invece di quello all'immagine su appunti
+
+
+ Configurazione Photobucket
+
+
+
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-ja-JP.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-ja-JP.xml
similarity index 100%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-ja-JP.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-ja-JP.xml
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-kab-DZ.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-kab-DZ.xml
similarity index 100%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-kab-DZ.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-kab-DZ.xml
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-ko-KR.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-ko-KR.xml
similarity index 100%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-ko-KR.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-ko-KR.xml
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-lv-LV.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-lv-LV.xml
similarity index 100%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-lv-LV.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-lv-LV.xml
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-nl-NL.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-nl-NL.xml
similarity index 96%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-nl-NL.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-nl-NL.xml
index 8756d62f5..dd63f662f 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-nl-NL.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-nl-NL.xml
@@ -1,29 +1,29 @@
-
-
-
-
- Naar Photobucket uploaden
-
-
- Photobucket instellingen
-
-
- Het uploaden naar Photobucket is geslaagt!
-
-
- Tijdens het uploaden naar Photobucket is een fout opgetreden:
-
-
- Beeld formaat
-
-
- Data overdraging naar Photobucket, wachten AUB...
-
-
- Kopieer de pagina link in plaats van de beeld link in het klembord
-
-
- Photobucket Instellingen
-
-
+
+
+
+
+ Naar Photobucket uploaden
+
+
+ Photobucket instellingen
+
+
+ Het uploaden naar Photobucket is geslaagt!
+
+
+ Tijdens het uploaden naar Photobucket is een fout opgetreden:
+
+
+ Beeld formaat
+
+
+ Data overdraging naar Photobucket, wachten AUB...
+
+
+ Kopieer de pagina link in plaats van de beeld link in het klembord
+
+
+ Photobucket Instellingen
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-pl-PL.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-pl-PL.xml
similarity index 98%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-pl-PL.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-pl-PL.xml
index 3ce17ca0e..6bd6f9c8e 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-pl-PL.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-pl-PL.xml
@@ -1,15 +1,15 @@
-
-
-
- Anuluj
- Trwa komunikacja z Photobucket. Proszę czekać...
- Konfiguruj Photobucket
- Format obrazów
- OK
- Ustawienia Photobucket
- Wystąpił błąd przy wysyłaniu do Photobucket:
- Wyślij do Photobucket
- Wysyłanie obrazu do Photobucket powiodło się!
- Kopiuj do schowka link do strony zamiast link do obrazu
-
+
+
+
+ Anuluj
+ Trwa komunikacja z Photobucket. Proszę czekać...
+ Konfiguruj Photobucket
+ Format obrazów
+ OK
+ Ustawienia Photobucket
+ Wystąpił błąd przy wysyłaniu do Photobucket:
+ Wyślij do Photobucket
+ Wysyłanie obrazu do Photobucket powiodło się!
+ Kopiuj do schowka link do strony zamiast link do obrazu
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-pt-PT.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-pt-PT.xml
similarity index 100%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-pt-PT.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-pt-PT.xml
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-ru-RU.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-ru-RU.xml
similarity index 98%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-ru-RU.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-ru-RU.xml
index 10532027e..756faf0a9 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-ru-RU.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-ru-RU.xml
@@ -1,15 +1,15 @@
-
-
-
- Отмена
- Обмен данными с Photobucket. Подождите...
- Настройка Photobucket
- Формат изображения
- OK
- Параметры Photobucket
- Произошла ошибка при загрузке на Photobucket:
- Загрузить на Photobucket
- Изображение успешно загружено на Photobucket!
- Использовать ссылку страницы, вместо ссылки изображения в буфере обмена
-
+
+
+
+ Отмена
+ Обмен данными с Photobucket. Подождите...
+ Настройка Photobucket
+ Формат изображения
+ OK
+ Параметры Photobucket
+ Произошла ошибка при загрузке на Photobucket:
+ Загрузить на Photobucket
+ Изображение успешно загружено на Photobucket!
+ Использовать ссылку страницы, вместо ссылки изображения в буфере обмена
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-sv-SE.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-sv-SE.xml
similarity index 100%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-sv-SE.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-sv-SE.xml
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-uk-UA.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-uk-UA.xml
similarity index 98%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-uk-UA.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-uk-UA.xml
index f85869978..c1f4e1732 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-uk-UA.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-uk-UA.xml
@@ -1,15 +1,15 @@
-
-
-
- Вивантажити на Photobucket
- Параметри Photobucket
- Гаразд
- Скасувати
- Зображення вдало вивантажено на Photobucket!
- Відбулась помилка при вивантаженні зображення на Photobucket:
- Формат зображення
- З’єднання з Photobucket. Будь ласка, зачекайте...
- Використовувати посилання на сторінку замість посилання на зображення
- Налаштувати Photobucket
-
-
+
+
+
+ Вивантажити на Photobucket
+ Параметри Photobucket
+ Гаразд
+ Скасувати
+ Зображення вдало вивантажено на Photobucket!
+ Відбулась помилка при вивантаженні зображення на Photobucket:
+ Формат зображення
+ З’єднання з Photobucket. Будь ласка, зачекайте...
+ Використовувати посилання на сторінку замість посилання на зображення
+ Налаштувати Photobucket
+
+
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-zh-CN.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-zh-CN.xml
similarity index 96%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-zh-CN.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-zh-CN.xml
index 5c0aae261..522424a35 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-zh-CN.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-zh-CN.xml
@@ -1,23 +1,23 @@
-
-
-
-
- 上传到 Photobucket
-
-
- Photobucket 设置
-
-
- 已成功上传图片到 Photobucket !
-
-
- 上传到 Photobucket 时出现错误:
-
-
- 图片格式
-
-
- 连接到 Photobucket,请稍候...
-
-
+
+
+
+
+ 上传到 Photobucket
+
+
+ Photobucket 设置
+
+
+ 已成功上传图片到 Photobucket !
+
+
+ 上传到 Photobucket 时出现错误:
+
+
+ 图片格式
+
+
+ 连接到 Photobucket,请稍候...
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-zh-TW.xml b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-zh-TW.xml
similarity index 98%
rename from src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-zh-TW.xml
rename to GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-zh-TW.xml
index caf649a89..c35408262 100644
--- a/src/Greenshot.Plugin.Photobucket/Languages/language_photobucket-zh-TW.xml
+++ b/GreenshotPhotobucketPlugin/Languages/language_photobucketplugin-zh-TW.xml
@@ -1,15 +1,15 @@
-
-
-
- 取消
- 正在與 Photobucket 通訊,請稍候...
- 組態 Photobucket
- 圖片格式
- 確定
- Photobucket 設定
- 上傳到 Photobucket 時發生錯誤:
- 上傳到 Photobucket
- 上傳圖片到 Photobucket 成功!
- 在剪貼簿使用空白連結而不是圖片連結
-
+
+
+
+ 取消
+ 正在與 Photobucket 通訊,請稍候...
+ 組態 Photobucket
+ 圖片格式
+ 確定
+ Photobucket 設定
+ 上傳到 Photobucket 時發生錯誤:
+ 上傳到 Photobucket
+ 上傳圖片到 Photobucket 成功!
+ 在剪貼簿使用空白連結而不是圖片連結
+
\ No newline at end of file
diff --git a/GreenshotPhotobucketPlugin/PhotobucketConfiguration.cs b/GreenshotPhotobucketPlugin/PhotobucketConfiguration.cs
new file mode 100644
index 000000000..7b00ad768
--- /dev/null
+++ b/GreenshotPhotobucketPlugin/PhotobucketConfiguration.cs
@@ -0,0 +1,73 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System.Windows.Forms;
+using GreenshotPlugin.Controls;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+
+namespace GreenshotPhotobucketPlugin {
+ ///
+ /// Description of PhotobucketConfiguration.
+ ///
+ [IniSection("Photobucket", Description="Greenshot Photobucket Plugin configuration")]
+ public class PhotobucketConfiguration : IniSection {
+ [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
+ public OutputFormat UploadFormat { get; set; }
+ [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
+ public int UploadJpegQuality { get; set; }
+ [IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")]
+ public bool UploadReduceColors { get; set; }
+ [IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")]
+ public bool UsePageLink { get; set; }
+ [IniProperty("Token", Description = "The Photobucket token", Encrypted=true, ExcludeIfNull=true)]
+ public string Token { get; set; }
+ [IniProperty("TokenSecret", Description = "The Photobucket token secret", Encrypted=true, ExcludeIfNull=true)]
+ public string TokenSecret { get; set; }
+ [IniProperty("SubDomain", Description = "The Photobucket api subdomain", Encrypted = true, ExcludeIfNull = true)]
+ public string SubDomain { get; set; }
+ [IniProperty("Username", Description = "The Photobucket api username", ExcludeIfNull = true)]
+ public string Username { get; set; }
+
+ public int Credits {
+ get;
+ set;
+ }
+
+ ///
+ /// A form for username/password
+ ///
+ /// bool true if OK was pressed, false if cancel
+ public bool ShowConfigDialog() {
+ SettingsForm settingsForm = null;
+
+ new PleaseWaitForm().ShowAndWait("Photobucket", Language.GetString("photobucket", LangKey.communication_wait),
+ delegate {
+ settingsForm = new SettingsForm();
+ }
+ );
+ DialogResult result = settingsForm.ShowDialog();
+ if (result == DialogResult.OK) {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/GreenshotPhotobucketPlugin/PhotobucketDestination.cs b/GreenshotPhotobucketPlugin/PhotobucketDestination.cs
new file mode 100644
index 000000000..9a22488bf
--- /dev/null
+++ b/GreenshotPhotobucketPlugin/PhotobucketDestination.cs
@@ -0,0 +1,101 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System.ComponentModel;
+using System.Collections.Generic;
+using System.Drawing;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+
+namespace GreenshotPhotobucketPlugin {
+ ///
+ /// Description of PhotobucketDestination.
+ ///
+ public class PhotobucketDestination : AbstractDestination {
+ private readonly PhotobucketPlugin _plugin;
+ private readonly string _albumPath;
+
+ ///
+ /// Create a Photobucket destination, which also has the path to the album in it
+ ///
+ ///
+ /// path to the album, null for default
+ public PhotobucketDestination(PhotobucketPlugin plugin, string albumPath = null) {
+ _plugin = plugin;
+ _albumPath = albumPath;
+ }
+
+ public override string Designation => "Photobucket";
+
+ public override string Description {
+ get {
+ if (_albumPath != null) {
+ return _albumPath;
+ }
+ return Language.GetString("photobucket", LangKey.upload_menu_item);
+ }
+ }
+
+ public override Image DisplayIcon {
+ get {
+ ComponentResourceManager resources = new ComponentResourceManager(typeof(PhotobucketPlugin));
+ return (Image)resources.GetObject("Photobucket");
+ }
+ }
+
+ public override bool IsDynamic => true;
+
+ public override IEnumerable DynamicDestinations() {
+ IList albums = null;
+ try {
+ albums = PhotobucketUtils.RetrievePhotobucketAlbums();
+ }
+ catch
+ {
+ // ignored
+ }
+
+ if (albums == null || albums.Count == 0) {
+ yield break;
+ }
+ foreach (string album in albums) {
+ yield return new PhotobucketDestination(_plugin, album);
+ }
+ }
+
+ ///
+ /// Export the capture to Photobucket
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description);
+ bool uploaded = _plugin.Upload(captureDetails, surface, _albumPath, out var uploadUrl);
+ if (uploaded) {
+ exportInformation.ExportMade = true;
+ exportInformation.Uri = uploadUrl;
+ }
+ ProcessExport(exportInformation, surface);
+ return exportInformation;
+ }
+ }
+}
diff --git a/GreenshotPhotobucketPlugin/PhotobucketInfo.cs b/GreenshotPhotobucketPlugin/PhotobucketInfo.cs
new file mode 100644
index 000000000..cf0fd0c54
--- /dev/null
+++ b/GreenshotPhotobucketPlugin/PhotobucketInfo.cs
@@ -0,0 +1,67 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Xml;
+
+namespace GreenshotPhotobucketPlugin
+{
+ ///
+ /// Description of PhotobucketInfo.
+ ///
+ public class PhotobucketInfo {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketInfo));
+
+ public string Original { get; set; }
+
+ public string Page { get; set; }
+
+ public string Thumbnail { get; set; }
+
+ ///
+ /// Parse the upload response
+ ///
+ /// XML
+ /// PhotobucketInfo object
+ public static PhotobucketInfo FromUploadResponse(string response) {
+ Log.Debug(response);
+ PhotobucketInfo photobucketInfo = new PhotobucketInfo();
+ try {
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(response);
+ var nodes = doc.GetElementsByTagName("url");
+ if(nodes.Count > 0) {
+ photobucketInfo.Original = nodes.Item(0)?.InnerText;
+ }
+ nodes = doc.GetElementsByTagName("browseurl");
+ if(nodes.Count > 0) {
+ photobucketInfo.Page = nodes.Item(0)?.InnerText;
+ }
+ nodes = doc.GetElementsByTagName("thumb");
+ if(nodes.Count > 0) {
+ photobucketInfo.Thumbnail = nodes.Item(0)?.InnerText;
+ }
+ } catch(Exception e) {
+ Log.ErrorFormat("Could not parse Photobucket response due to error {0}, response was: {1}", e.Message, response);
+ }
+ return photobucketInfo;
+ }
+ }
+}
diff --git a/GreenshotPhotobucketPlugin/PhotobucketPlugin.cs b/GreenshotPhotobucketPlugin/PhotobucketPlugin.cs
new file mode 100644
index 000000000..971ccc0e6
--- /dev/null
+++ b/GreenshotPhotobucketPlugin/PhotobucketPlugin.cs
@@ -0,0 +1,141 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+using GreenshotPlugin.Controls;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotPhotobucketPlugin {
+ ///
+ /// This is the GreenshotPhotobucketPlugin base code
+ ///
+ [Plugin("Photobucket", true)]
+ public class PhotobucketPlugin : IGreenshotPlugin {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketPlugin));
+ private static PhotobucketConfiguration _config;
+ private ComponentResourceManager _resources;
+ private ToolStripMenuItem _itemPlugInConfig;
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected void Dispose(bool disposing)
+ {
+ if (!disposing) return;
+ if (_itemPlugInConfig == null) return;
+ _itemPlugInConfig.Dispose();
+ _itemPlugInConfig = null;
+ }
+
+ ///
+ /// Implementation of the IGreenshotPlugin.Initialize
+ ///
+ /// true if plugin is initialized, false if not (doesn't show)
+ public bool Initialize() {
+ SimpleServiceProvider.Current.AddService(new PhotobucketDestination(this));
+
+ // Get configuration
+ _config = IniConfig.GetIniSection();
+ _resources = new ComponentResourceManager(typeof(PhotobucketPlugin));
+
+ _itemPlugInConfig = new ToolStripMenuItem(Language.GetString("photobucket", LangKey.configure))
+ {
+ Image = (Image)_resources.GetObject("Photobucket")
+ };
+ _itemPlugInConfig.Click += delegate {
+ _config.ShowConfigDialog();
+ };
+
+ PluginUtils.AddToContextMenu(_itemPlugInConfig);
+ Language.LanguageChanged += OnLanguageChanged;
+ return true;
+ }
+
+ public void OnLanguageChanged(object sender, EventArgs e) {
+ if (_itemPlugInConfig != null) {
+ _itemPlugInConfig.Text = Language.GetString("photobucket", LangKey.configure);
+ }
+ }
+
+ public void Shutdown() {
+ Log.Debug("Photobucket Plugin shutdown.");
+ Language.LanguageChanged -= OnLanguageChanged;
+ }
+
+ ///
+ /// Implementation of the IPlugin.Configure
+ ///
+ public void Configure() {
+ _config.ShowConfigDialog();
+ }
+
+ ///
+ /// Upload the capture to Photobucket
+ ///
+ ///
+ /// ISurface
+ /// Path to the album
+ /// out string for the url
+ /// true if the upload succeeded
+ public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, string albumPath, out string uploadUrl) {
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors);
+ try {
+ string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
+ PhotobucketInfo photobucketInfo = null;
+
+ // Run upload in the background
+ new PleaseWaitForm().ShowAndWait("Photobucket", Language.GetString("photobucket", LangKey.communication_wait),
+ delegate {
+ photobucketInfo = PhotobucketUtils.UploadToPhotobucket(surfaceToUpload, outputSettings, albumPath, captureDetails.Title, filename);
+ }
+ );
+ // This causes an exeption if the upload failed :)
+ Log.DebugFormat("Uploaded to Photobucket page: " + photobucketInfo.Page);
+ uploadUrl = null;
+ try {
+ if (_config.UsePageLink) {
+ uploadUrl = photobucketInfo.Page;
+ Clipboard.SetText(photobucketInfo.Page);
+ } else {
+ uploadUrl = photobucketInfo.Original;
+ Clipboard.SetText(photobucketInfo.Original);
+ }
+ } catch (Exception ex) {
+ Log.Error("Can't write to clipboard: ", ex);
+ }
+ return true;
+ } catch (Exception e) {
+ Log.Error(e);
+ MessageBox.Show(Language.GetString("photobucket", LangKey.upload_failure) + " " + e.Message);
+ }
+ uploadUrl = null;
+ return false;
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Photobucket/PhotobucketPlugin.resx b/GreenshotPhotobucketPlugin/PhotobucketPlugin.resx
similarity index 100%
rename from src/Greenshot.Plugin.Photobucket/PhotobucketPlugin.resx
rename to GreenshotPhotobucketPlugin/PhotobucketPlugin.resx
diff --git a/GreenshotPhotobucketPlugin/PhotobucketUtils.cs b/GreenshotPhotobucketPlugin/PhotobucketUtils.cs
new file mode 100644
index 000000000..b681edac7
--- /dev/null
+++ b/GreenshotPhotobucketPlugin/PhotobucketUtils.cs
@@ -0,0 +1,224 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Xml;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotPhotobucketPlugin {
+ ///
+ /// Description of PhotobucketUtils.
+ ///
+ public static class PhotobucketUtils {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketUtils));
+ private static readonly PhotobucketConfiguration PhotobucketConfig = IniConfig.GetIniSection();
+ private static List _albumsCache;
+
+ ///
+ /// Do the actual upload to Photobucket
+ /// For more details on the available parameters, see: http://api.Photobucket.com/resources_anon
+ ///
+ /// PhotobucketResponse
+ public static PhotobucketInfo UploadToPhotobucket(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string albumPath, string title, string filename) {
+ string responseString;
+
+ var oAuth = CreateSession(true);
+ if (oAuth == null) {
+ return null;
+ }
+ IDictionary signedParameters = new Dictionary();
+ // add album
+ if (string.IsNullOrEmpty(albumPath))
+ {
+ signedParameters.Add("id", string.IsNullOrEmpty(PhotobucketConfig.Username) ? "!" : PhotobucketConfig.Username);
+ } else {
+ signedParameters.Add("id", albumPath);
+ }
+ // add type
+ signedParameters.Add("type", "image");
+ // add title
+ if (title != null) {
+ signedParameters.Add("title", title);
+ }
+ // add filename
+ if (filename != null) {
+ signedParameters.Add("filename", filename);
+ }
+ IDictionary unsignedParameters = new Dictionary
+ {
+ // Add image
+ { "uploadfile", new SurfaceContainer(surfaceToUpload, outputSettings, filename) }
+ };
+ try {
+ string apiUrl = "http://api.photobucket.com/album/!/upload";
+ responseString = oAuth.MakeOAuthRequest(HTTPMethod.POST, apiUrl, apiUrl.Replace("api.photobucket.com", PhotobucketConfig.SubDomain), signedParameters, unsignedParameters, null);
+ } catch (Exception ex) {
+ Log.Error("Error uploading to Photobucket.", ex);
+ throw;
+ } finally {
+ if (!string.IsNullOrEmpty(oAuth.Token)) {
+ PhotobucketConfig.Token = oAuth.Token;
+ }
+ if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
+ PhotobucketConfig.TokenSecret = oAuth.TokenSecret;
+ }
+ }
+ if (responseString == null) {
+ return null;
+ }
+ Log.Info(responseString);
+ var photobucketInfo = PhotobucketInfo.FromUploadResponse(responseString);
+ Log.Debug("Upload to Photobucket was finished");
+ return photobucketInfo;
+ }
+
+ ///
+ /// Helper method to create an OAuth session object for contacting the Photobucket API
+ ///
+ /// OAuthSession
+ private static OAuthSession CreateSession(bool autoLogin) {
+ var oAuth = new OAuthSession(PhotobucketCredentials.ConsumerKey, PhotobucketCredentials.ConsumerSecret)
+ {
+ AutoLogin = autoLogin,
+ CheckVerifier = false,
+ CallbackUrl = "http://getgreenshot.org",
+ AccessTokenUrl = "http://api.photobucket.com/login/access",
+ AuthorizeUrl = "http://photobucket.com/apilogin/login",
+ RequestTokenUrl = "http://api.photobucket.com/login/request",
+ BrowserSize = new Size(1010, 400),
+ RequestTokenMethod = HTTPMethod.POST,
+ AccessTokenMethod = HTTPMethod.POST,
+ LoginTitle = "Photobucket authorization"
+ };
+ // This url is configured in the Photobucket API settings in the Photobucket site!!
+ // Photobucket is very particular about the used methods!
+
+ if (string.IsNullOrEmpty(PhotobucketConfig.SubDomain) || string.IsNullOrEmpty(PhotobucketConfig.Token) || string.IsNullOrEmpty(PhotobucketConfig.Username)) {
+ if (!autoLogin) {
+ return null;
+ }
+ if (!oAuth.Authorize()) {
+ return null;
+ }
+ if (!string.IsNullOrEmpty(oAuth.Token)) {
+ PhotobucketConfig.Token = oAuth.Token;
+ }
+ if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
+ PhotobucketConfig.TokenSecret = oAuth.TokenSecret;
+ }
+ if (oAuth.AccessTokenResponseParameters?["subdomain"] != null) {
+ PhotobucketConfig.SubDomain = oAuth.AccessTokenResponseParameters["subdomain"];
+ }
+ if (oAuth.AccessTokenResponseParameters?["username"] != null) {
+ PhotobucketConfig.Username = oAuth.AccessTokenResponseParameters["username"];
+ }
+ IniConfig.Save();
+ }
+ oAuth.Token = PhotobucketConfig.Token;
+ oAuth.TokenSecret = PhotobucketConfig.TokenSecret;
+ return oAuth;
+ }
+
+ ///
+ /// Get list of photobucket albums
+ ///
+ /// List of string
+ public static IList RetrievePhotobucketAlbums() {
+ if (_albumsCache != null) {
+ return _albumsCache;
+ }
+ string responseString;
+
+ OAuthSession oAuth = CreateSession(false);
+ if (oAuth == null) {
+ return null;
+ }
+ IDictionary signedParameters = new Dictionary();
+ try {
+ string apiUrl = $"http://api.photobucket.com/album/{PhotobucketConfig.Username}";
+ responseString = oAuth.MakeOAuthRequest(HTTPMethod.GET, apiUrl, apiUrl.Replace("api.photobucket.com", PhotobucketConfig.SubDomain), signedParameters, null, null);
+ } catch (Exception ex) {
+ Log.Error("Error uploading to Photobucket.", ex);
+ throw;
+ } finally {
+ if (!string.IsNullOrEmpty(oAuth.Token)) {
+ PhotobucketConfig.Token = oAuth.Token;
+ }
+ if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
+ PhotobucketConfig.TokenSecret = oAuth.TokenSecret;
+ }
+ }
+ if (responseString == null) {
+ return null;
+ }
+ try {
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(responseString);
+ List albums = new List();
+ var xmlNode = doc.GetElementsByTagName("content").Item(0);
+ if (xmlNode != null)
+ {
+ RecurseAlbums(albums, null, xmlNode.ChildNodes);
+ }
+ Log.DebugFormat("Albums: {0}", string.Join(",", albums.ToArray()));
+ _albumsCache = albums;
+ return albums;
+ } catch(Exception e) {
+ Log.Error("Error while Reading albums: ", e);
+ }
+
+ Log.Debug("Upload to Photobucket was finished");
+ return null;
+ }
+
+ ///
+ /// Parse the album nodes recursively
+ ///
+ ///
+ ///
+ ///
+ private static void RecurseAlbums(ICollection albums, string path, IEnumerable nodes) {
+ foreach(XmlNode node in nodes) {
+ if (node.Name != "album") {
+ continue;
+ }
+ if (node.Attributes != null)
+ {
+ string currentAlbum = node.Attributes["name"].Value;
+ string currentPath = currentAlbum;
+ if (!string.IsNullOrEmpty(path)) {
+ currentPath = $"{path}/{currentAlbum}";
+ }
+
+ albums.Add(currentPath);
+ if (node.Attributes["subalbum_count"] != null && node.Attributes["subalbum_count"].Value != "0") {
+ RecurseAlbums(albums, currentPath, node.ChildNodes);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs b/GreenshotPhotobucketPlugin/Properties/AssemblyInfo.cs
similarity index 79%
rename from src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs
rename to GreenshotPhotobucketPlugin/Properties/AssemblyInfo.cs
index 231ca594d..9aa4884a4 100644
--- a/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs
+++ b/GreenshotPhotobucketPlugin/Properties/AssemblyInfo.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,20 +16,19 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
using System.Reflection;
using System.Runtime.InteropServices;
-using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to Photobucket")]
-[assembly: AssemblyPluginIdentifier("Photobucket Plugin")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
-[assembly: ComVisible(false)]
\ No newline at end of file
+[assembly: ComVisible(false)]
+
diff --git a/src/Greenshot.Plugin.GooglePhotos/Forms/GooglePhotosForm.cs b/GreenshotPicasaPlugin/Forms/PicasaForm.cs
similarity index 66%
rename from src/Greenshot.Plugin.GooglePhotos/Forms/GooglePhotosForm.cs
rename to GreenshotPicasaPlugin/Forms/PicasaForm.cs
index 84471d011..a1d093e30 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Forms/GooglePhotosForm.cs
+++ b/GreenshotPicasaPlugin/Forms/PicasaForm.cs
@@ -1,8 +1,8 @@
/*
- * A GooglePhotos Plugin for Greenshot
+ * A Picasa Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,14 +15,11 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using Greenshot.Base.Controls;
-
-namespace Greenshot.Plugin.GooglePhotos.Forms
-{
- public class GooglePhotosForm : GreenshotForm
- {
- }
-}
\ No newline at end of file
+ * along with this program. If not, see .
+ */
+using GreenshotPlugin.Controls;
+
+namespace GreenshotPicasaPlugin {
+ public class PicasaForm : GreenshotForm {
+ }
+}
diff --git a/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs b/GreenshotPicasaPlugin/Forms/SettingsForm.Designer.cs
similarity index 74%
rename from src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs
rename to GreenshotPicasaPlugin/Forms/SettingsForm.Designer.cs
index 23e68012f..41cb346f9 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs
+++ b/GreenshotPicasaPlugin/Forms/SettingsForm.Designer.cs
@@ -1,150 +1,148 @@
-/*
- * A GooglePhotos Plugin for Greenshot
- * Copyright (C) 2011 Francis Noel
- *
- * For more information see: https://getgreenshot.org/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using Greenshot.Base.Controls;
-
-namespace Greenshot.Plugin.GooglePhotos.Forms {
- partial class SettingsForm {
- ///
- /// Designer variable used to keep track of non-visual components.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Disposes resources used by the form.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing) {
- if (components != null) {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
-
- ///
- /// This method is required for Windows Forms designer support.
- /// Do not change the method contents inside the source code editor. The Forms designer might
- /// not be able to load this method if it was changed manually.
- ///
- private void InitializeComponent()
- {
- this.buttonOK = new GreenshotButton();
- this.buttonCancel = new GreenshotButton();
- this.combobox_uploadimageformat = new GreenshotComboBox();
- this.label_upload_format = new GreenshotLabel();
- this.label_AfterUpload = new GreenshotLabel();
- this.checkboxAfterUploadLinkToClipBoard = new GreenshotCheckBox();
- this.SuspendLayout();
- //
- // buttonOK
- //
- this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK;
- this.buttonOK.LanguageKey = "OK";
- this.buttonOK.Location = new System.Drawing.Point(267, 78);
- this.buttonOK.Name = "buttonOK";
- this.buttonOK.Size = new System.Drawing.Size(75, 23);
- this.buttonOK.TabIndex = 10;
- this.buttonOK.UseVisualStyleBackColor = true;
- //
- // buttonCancel
- //
- this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.buttonCancel.LanguageKey = "CANCEL";
- this.buttonCancel.Location = new System.Drawing.Point(348, 78);
- this.buttonCancel.Name = "buttonCancel";
- this.buttonCancel.Size = new System.Drawing.Size(75, 23);
- this.buttonCancel.TabIndex = 11;
- this.buttonCancel.UseVisualStyleBackColor = true;
- //
- // combobox_uploadimageformat
- //
- this.combobox_uploadimageformat.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.combobox_uploadimageformat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
- this.combobox_uploadimageformat.FormattingEnabled = true;
- this.combobox_uploadimageformat.Location = new System.Drawing.Point(197, 12);
- this.combobox_uploadimageformat.Name = "combobox_uploadimageformat";
- this.combobox_uploadimageformat.PropertyName = nameof(GooglePhotosConfiguration.UploadFormat);
- this.combobox_uploadimageformat.SectionName = "GooglePhotos";
- this.combobox_uploadimageformat.Size = new System.Drawing.Size(225, 21);
- this.combobox_uploadimageformat.TabIndex = 1;
- //
- // label_upload_format
- //
- this.label_upload_format.LanguageKey = "googlephotos.label_upload_format";
- this.label_upload_format.Location = new System.Drawing.Point(10, 18);
- this.label_upload_format.Name = "label_upload_format";
- this.label_upload_format.Size = new System.Drawing.Size(181, 33);
- this.label_upload_format.TabIndex = 4;
- //
- // label_AfterUpload
- //
- this.label_AfterUpload.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.label_AfterUpload.LanguageKey = "googlephotos.label_AfterUpload";
- this.label_AfterUpload.Location = new System.Drawing.Point(10, 51);
- this.label_AfterUpload.Name = "label_AfterUpload";
- this.label_AfterUpload.Size = new System.Drawing.Size(181, 29);
- this.label_AfterUpload.TabIndex = 8;
- //
- // checkboxAfterUploadLinkToClipBoard
- //
- this.checkboxAfterUploadLinkToClipBoard.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "googlephotos.label_AfterUploadLinkToClipBoard";
- this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(197, 50);
- this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard";
- this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(GooglePhotosConfiguration.AfterUploadLinkToClipBoard);
- this.checkboxAfterUploadLinkToClipBoard.SectionName = "GooglePhotos";
- this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17);
- this.checkboxAfterUploadLinkToClipBoard.TabIndex = 2;
- this.checkboxAfterUploadLinkToClipBoard.UseVisualStyleBackColor = true;
- //
- // SettingsForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(432, 110);
- this.Controls.Add(this.checkboxAfterUploadLinkToClipBoard);
- this.Controls.Add(this.label_AfterUpload);
- this.Controls.Add(this.label_upload_format);
- this.Controls.Add(this.combobox_uploadimageformat);
- this.Controls.Add(this.buttonCancel);
- this.Controls.Add(this.buttonOK);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.LanguageKey = "googlephotos.settings_title";
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "SettingsForm";
- this.ResumeLayout(false);
- this.PerformLayout();
-
- }
- private GreenshotComboBox combobox_uploadimageformat;
- private GreenshotLabel label_upload_format;
- private GreenshotButton buttonCancel;
- private GreenshotButton buttonOK;
- private GreenshotLabel label_AfterUpload;
- private GreenshotCheckBox checkboxAfterUploadLinkToClipBoard;
- }
-}
+/*
+ * A Picasa Plugin for Greenshot
+ * Copyright (C) 2011 Francis Noel
+ *
+ * For more information see: http://getgreenshot.org/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace GreenshotPicasaPlugin {
+ partial class SettingsForm {
+ ///
+ /// Designer variable used to keep track of non-visual components.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Disposes resources used by the form.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing) {
+ if (components != null) {
+ components.Dispose();
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// This method is required for Windows Forms designer support.
+ /// Do not change the method contents inside the source code editor. The Forms designer might
+ /// not be able to load this method if it was changed manually.
+ ///
+ private void InitializeComponent()
+ {
+ this.buttonOK = new GreenshotPlugin.Controls.GreenshotButton();
+ this.buttonCancel = new GreenshotPlugin.Controls.GreenshotButton();
+ this.combobox_uploadimageformat = new GreenshotPlugin.Controls.GreenshotComboBox();
+ this.label_upload_format = new GreenshotPlugin.Controls.GreenshotLabel();
+ this.label_AfterUpload = new GreenshotPlugin.Controls.GreenshotLabel();
+ this.checkboxAfterUploadLinkToClipBoard = new GreenshotPlugin.Controls.GreenshotCheckBox();
+ this.SuspendLayout();
+ //
+ // buttonOK
+ //
+ this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK;
+ this.buttonOK.LanguageKey = "OK";
+ this.buttonOK.Location = new System.Drawing.Point(267, 78);
+ this.buttonOK.Name = "buttonOK";
+ this.buttonOK.Size = new System.Drawing.Size(75, 23);
+ this.buttonOK.TabIndex = 10;
+ this.buttonOK.UseVisualStyleBackColor = true;
+ //
+ // buttonCancel
+ //
+ this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.buttonCancel.LanguageKey = "CANCEL";
+ this.buttonCancel.Location = new System.Drawing.Point(348, 78);
+ this.buttonCancel.Name = "buttonCancel";
+ this.buttonCancel.Size = new System.Drawing.Size(75, 23);
+ this.buttonCancel.TabIndex = 11;
+ this.buttonCancel.UseVisualStyleBackColor = true;
+ //
+ // combobox_uploadimageformat
+ //
+ this.combobox_uploadimageformat.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.combobox_uploadimageformat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.combobox_uploadimageformat.FormattingEnabled = true;
+ this.combobox_uploadimageformat.Location = new System.Drawing.Point(197, 12);
+ this.combobox_uploadimageformat.Name = "combobox_uploadimageformat";
+ this.combobox_uploadimageformat.PropertyName = "UploadFormat";
+ this.combobox_uploadimageformat.SectionName = "Picasa";
+ this.combobox_uploadimageformat.Size = new System.Drawing.Size(225, 21);
+ this.combobox_uploadimageformat.TabIndex = 1;
+ //
+ // label_upload_format
+ //
+ this.label_upload_format.LanguageKey = "picasa.label_upload_format";
+ this.label_upload_format.Location = new System.Drawing.Point(10, 18);
+ this.label_upload_format.Name = "label_upload_format";
+ this.label_upload_format.Size = new System.Drawing.Size(181, 33);
+ this.label_upload_format.TabIndex = 4;
+ //
+ // label_AfterUpload
+ //
+ this.label_AfterUpload.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label_AfterUpload.LanguageKey = "picasa.label_AfterUpload";
+ this.label_AfterUpload.Location = new System.Drawing.Point(10, 51);
+ this.label_AfterUpload.Name = "label_AfterUpload";
+ this.label_AfterUpload.Size = new System.Drawing.Size(181, 29);
+ this.label_AfterUpload.TabIndex = 8;
+ //
+ // checkboxAfterUploadLinkToClipBoard
+ //
+ this.checkboxAfterUploadLinkToClipBoard.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.checkboxAfterUploadLinkToClipBoard.AutoSize = true;
+ this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "picasa.label_AfterUploadLinkToClipBoard";
+ this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(197, 50);
+ this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard";
+ this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard";
+ this.checkboxAfterUploadLinkToClipBoard.SectionName = "Picasa";
+ this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17);
+ this.checkboxAfterUploadLinkToClipBoard.TabIndex = 2;
+ this.checkboxAfterUploadLinkToClipBoard.UseVisualStyleBackColor = true;
+ //
+ // SettingsForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.ClientSize = new System.Drawing.Size(432, 110);
+ this.Controls.Add(this.checkboxAfterUploadLinkToClipBoard);
+ this.Controls.Add(this.label_AfterUpload);
+ this.Controls.Add(this.label_upload_format);
+ this.Controls.Add(this.combobox_uploadimageformat);
+ this.Controls.Add(this.buttonCancel);
+ this.Controls.Add(this.buttonOK);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.LanguageKey = "picasa.settings_title";
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "SettingsForm";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+ private GreenshotPlugin.Controls.GreenshotComboBox combobox_uploadimageformat;
+ private GreenshotPlugin.Controls.GreenshotLabel label_upload_format;
+ private GreenshotPlugin.Controls.GreenshotButton buttonCancel;
+ private GreenshotPlugin.Controls.GreenshotButton buttonOK;
+ private GreenshotPlugin.Controls.GreenshotLabel label_AfterUpload;
+ private GreenshotPlugin.Controls.GreenshotCheckBox checkboxAfterUploadLinkToClipBoard;
+ }
+}
diff --git a/GreenshotPicasaPlugin/Forms/SettingsForm.cs b/GreenshotPicasaPlugin/Forms/SettingsForm.cs
new file mode 100644
index 000000000..734559762
--- /dev/null
+++ b/GreenshotPicasaPlugin/Forms/SettingsForm.cs
@@ -0,0 +1,38 @@
+/*
+ * A Picasa Plugin for Greenshot
+ * Copyright (C) 2011 Francis Noel
+ *
+ * For more information see: http://getgreenshot.org/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotPicasaPlugin {
+ ///
+ /// Description of PasswordRequestForm.
+ ///
+ public partial class SettingsForm : PicasaForm {
+
+ public SettingsForm()
+ {
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+ CancelButton = buttonCancel;
+ AcceptButton = buttonOK;
+ }
+
+ }
+}
diff --git a/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.Credentials.template b/GreenshotPicasaPlugin/GreenshotPicasaPlugin.Credentials.template
similarity index 78%
rename from src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.Credentials.template
rename to GreenshotPicasaPlugin/GreenshotPicasaPlugin.Credentials.template
index c4ff6bfc6..5cb7a2aac 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Greenshot.Plugin.GooglePhotos.Credentials.template
+++ b/GreenshotPicasaPlugin/GreenshotPicasaPlugin.Credentials.template
@@ -1,6 +1,6 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
@@ -19,13 +19,13 @@
* along with this program. If not, see .
*/
-namespace Greenshot.Plugin.GooglePhotos {
+namespace GreenshotPicasaPlugin {
///
/// This class is merely a placeholder for the file keeping the API key and secret for dropbox integration.
/// You can set your own values here
///
- public static class GooglePhotosCredentials {
- public static string ClientId = "${GooglePhotos_ClientId}";
- public static string ClientSecret = "${GooglePhotos_ClientSecret}";
+ public static class PicasaCredentials {
+ public static string ClientId = "${Picasa_ClientId}";
+ public static string ClientSecret = "${Picasa_ClientSecret}";
}
}
diff --git a/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj b/GreenshotPicasaPlugin/GreenshotPicasaPlugin.csproj
similarity index 52%
rename from src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj
rename to GreenshotPicasaPlugin/GreenshotPicasaPlugin.csproj
index 2b3410c8f..ac827cfec 100644
--- a/src/Greenshot.Plugin.Box/Greenshot.Plugin.Box.csproj
+++ b/GreenshotPicasaPlugin/GreenshotPicasaPlugin.csproj
@@ -1,8 +1,10 @@
-
- none
- false
+
+
+ GreenshotPicasaPlugin
+ GreenshotPicasaPlugin
+
PreserveNewest
@@ -10,10 +12,10 @@
-
+
-
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/LanguageKeys.cs b/GreenshotPicasaPlugin/LanguageKeys.cs
similarity index 60%
rename from src/Greenshot.Plugin.GooglePhotos/LanguageKeys.cs
rename to GreenshotPicasaPlugin/LanguageKeys.cs
index 5255daac3..58a488db5 100644
--- a/src/Greenshot.Plugin.GooglePhotos/LanguageKeys.cs
+++ b/GreenshotPicasaPlugin/LanguageKeys.cs
@@ -1,8 +1,8 @@
/*
- * A GooglePhotos Plugin for Greenshot
+ * A Picasa Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,16 +15,20 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-namespace Greenshot.Plugin.GooglePhotos
-{
- public enum LangKey
- {
- upload_menu_item,
- upload_failure,
- communication_wait,
- Configure
- }
-}
\ No newline at end of file
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotPicasaPlugin {
+ public enum LangKey
+ {
+ upload_menu_item,
+ settings_title,
+ label_upload_format,
+ upload_success,
+ upload_failure,
+ communication_wait,
+ Configure,
+ label_AfterUpload,
+ label_AfterUploadLinkToClipBoard
+ }
+}
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-cs-CZ.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-cs-CZ.xml
similarity index 74%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-cs-CZ.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-cs-CZ.xml
index 7168b569c..4fe14494b 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-cs-CZ.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-cs-CZ.xml
@@ -1,29 +1,29 @@
-
-
-
- Zrušit
- Probíhá komunikace s Picasem. Prosím počkejte ...
- Konfigurace
- Opravdu chcete odstranit obrázek {0} z GooglePhotos?
- Odstranit GooglePhotos {0}
- Historie
- Neplatná oprávnění. Otevřít nastavení pro provedené změn.
- Po odeslání
- Kopírovat odkaz do schránky
- Zobrazit historii
- Výchozí velikost
- Heslo
- Formát obrázku
- Jméno
- OK
- Originál URL
- Čtvercové náhledy URL ???
- Webová adresa URL
- Nastavení GooglePhotos
- Nahrát
- Nahrání obrázku do GooglePhotos se nezdařilo:
- Nahrát do GooglePhotos
- Úspěšně odeslaný obrázek do GooglePhotos!
- Prosím ověřit aplikaci GooglePhotos. Otevřít nastavení obrazovky. ???
-
+
+
+
+ Zrušit
+ Probíhá komunikace s Picasem. Prosím počkejte ...
+ Konfigurace
+ Opravdu chcete odstranit obrázek {0} z Picasa?
+ Odstranit Picasa {0}
+ Historie
+ Neplatná oprávnění. Otevřít nastavení pro provedené změn.
+ Po odeslání
+ Kopírovat odkaz do schránky
+ Zobrazit historii
+ Výchozí velikost
+ Heslo
+ Formát obrázku
+ Jméno
+ OK
+ Originál URL
+ Čtvercové náhledy URL ???
+ Webová adresa URL
+ Nastavení Picasa
+ Nahrát
+ Nahrání obrázku do Picasa se nezdařilo:
+ Nahrát do Picasa
+ Úspěšně odeslaný obrázek do Picasa!
+ Prosím ověřit aplikaci Picasa. Otevřít nastavení obrazovky. ???
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-de-DE.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-de-DE.xml
similarity index 74%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-de-DE.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-de-DE.xml
index 0a437a443..6b30f0214 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-de-DE.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-de-DE.xml
@@ -8,25 +8,25 @@
Anschliessend
- GooglePhotos konfigurieren
+ Picasa konfigurieren
- Hochladen zu GooglePhotos
+ Hochladen zu Picasa
- GooglePhotos Einstellungen
+ Picasa Einstellungen
- Hochladen zu GooglePhotos war erfolgreich !
+ Hochladen zu Picasa war erfolgreich !
- Fehler beim Hochladen zu GooglePhotos:
+ Fehler beim Hochladen zu Picasa:
Grafikformat
- Übermittle Daten zu GooglePhotos. Bitte warten...
+ Übermittle Daten zu Picasa. Bitte warten...
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-en-US.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-en-US.xml
similarity index 73%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-en-US.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-en-US.xml
index 76c477d50..e99c84ed7 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-en-US.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-en-US.xml
@@ -8,25 +8,25 @@
After upload
- Configure GooglePhotos
+ Configure Picasa
- Upload to GooglePhotos
+ Upload to Picasa
- GooglePhotos settings
+ Picasa settings
- Successfully uploaded image to GooglePhotos!
+ Successfully uploaded image to Picasa!
- An error occurred while uploading to GooglePhotos:
+ An error occured while uploading to Picasa:
Image format
- Communicating with GooglePhotos. Please wait...
+ Communicating with Picasa. Please wait...
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-fr-FR.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-fr-FR.xml
similarity index 62%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-fr-FR.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-fr-FR.xml
index 553d5725e..589d55613 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-fr-FR.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-fr-FR.xml
@@ -1,14 +1,14 @@
-
-
-
- Communication en cours avec GooglePhotos. Veuillez patientez...
- Configurer GooglePhotos
- Après téléversement
- Copier le lien dans le presse-papier
- Format image
- Paramètres GooglePhotos
- Une erreur s'est produite lors du téléversement vers GooglePhotos :
- Téléverser vers GooglePhotos
- Image téléversée avec succès vers GooglePhotos !
-
+
+
+
+ Communication en cours avec Picasa. Veuillez patientez...
+ Configurer Picasa
+ Après téléversement
+ Copier le lien dans le presse-papier
+ Format image
+ Paramètres Picasa
+ Une erreur s'est produite lors du téléversement vers Picasa :
+ Téléverser vers Picasa
+ Image téléversée avec succès vers Picasa !
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-id-ID.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-id-ID.xml
similarity index 58%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-id-ID.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-id-ID.xml
index c906bb08e..5632a3daa 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-id-ID.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-id-ID.xml
@@ -1,14 +1,14 @@
-
-
-
- Menyambungkan ke GooglePhotos. Tunggu sebentar...
- Konfigurasi GooglePhotos
- Sesudah mengunggah
- Sambung ke papanklip
- Format gambar
- Pengaturan GooglePhotos
- Kesalahan terjadi ketika mengunggah ke GooglePhotos:
- Unggah ke GooglePhotos
- Berhasil mengunggah gambar ke GooglePhotos!
-
+
+
+
+ Menyambungkan ke Picasa. Tunggu sebentar...
+ Konfigurasi Picasa
+ Sesudah mengunggah
+ Sambung ke papanklip
+ Format gambar
+ Pengaturan Picasa
+ Kesalahan terjadi ketika mengunggah ke Picasa:
+ Unggah ke Picasa
+ Berhasil mengunggah gambar ke Picasa!
+
\ No newline at end of file
diff --git a/GreenshotPicasaPlugin/Languages/language_picasaplugin-it-IT.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-it-IT.xml
new file mode 100644
index 000000000..a72279d00
--- /dev/null
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-it-IT.xml
@@ -0,0 +1,14 @@
+
+
+
+ Comunicazione con Picasa. Attendere prego...
+ Configurazione Picasa
+ Dopo il carico
+ Collegamento agli appunti
+ Formato immagine
+ Impostazioni di Picasa
+ Si è verificato un errore durante il caricamento su Picasa:
+ Carica su Picasa
+ Immagine caricata correttamente su Picasa!
+
+
\ No newline at end of file
diff --git a/GreenshotPicasaPlugin/Languages/language_picasaplugin-ja-JP.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-ja-JP.xml
new file mode 100644
index 000000000..ac8216e96
--- /dev/null
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-ja-JP.xml
@@ -0,0 +1,14 @@
+
+
+
+ リンクをクリップボードへコピー
+ アップロード後
+ Picasa の設定
+ Picasa にアップロード
+ Picasa の設定
+ Picasa へのアップロードに成功しました!
+ Picasa へのアップロード中にエラーが発生しました:
+ 画像フォーマット
+ Picasa に接続中です。しばらくお待ち下さい...
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-kab-DZ.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-kab-DZ.xml
similarity index 59%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-kab-DZ.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-kab-DZ.xml
index dd5c784d4..367287108 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-kab-DZ.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-kab-DZ.xml
@@ -1,14 +1,14 @@
- S tidett tebɣiḍ ad tekkseḍ amazray adigan n GooglePhotos ?...
- Swel GooglePhotos
+ S tidett tebɣiḍ ad tekkseḍ amazray adigan n Picasa ?...
+ Swel PicasaTicki yemmed usaliNɣel aseɣwen ɣef afusAmsal n tugna
- Iɣewwaṛen GooglePhotos
- Teḍra-d tuccḍa deg usali ɣer GooglePhotos :
- Sali ɣer GooglePhotos
- Tugna tuli ɣer GooglePhotos !
+ Iɣewwaṛen Picasa
+ Teḍra-d tuccḍa deg usali ɣer Picasa :
+ Sali ɣer Picasa
+ Tugna tuli ɣer Picasa !
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-ko-KR.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-ko-KR.xml
similarity index 74%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-ko-KR.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-ko-KR.xml
index d095f9f72..f452a9618 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-ko-KR.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-ko-KR.xml
@@ -8,25 +8,25 @@
얼로드 후
- GooglePhotos 설정
+ Picasa 설정
- GooglePhotos로 업로드
+ Picasa로 업로드
- GooglePhotos 설정
+ Picasa 설정
- GooglePhotos로 이미지 업로드 성공!
+ Picasa로 이미지 업로드 성공!
- GooglePhotos로 업로드시 오류 발생:
+ Picasa로 업로드시 오류 발생:
이미지 형식
- GooglePhotos와 연결 중 잠시 기다리세요...
+ Picasa와 연결 중 잠시 기다리세요...
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-lv-LV.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-lv-LV.xml
similarity index 75%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-lv-LV.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-lv-LV.xml
index 7e0e0b8ca..c33bdc519 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-lv-LV.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-lv-LV.xml
@@ -9,25 +9,25 @@
Pēc augšupielādes
- GooglePhotos iestatījumi
+ Picasa iestatījumi
- Augšupieladēt uz GooglePhotos
+ Augšupieladēt uz Picasa
- GooglePhotos iestatījumi
+ Picasa iestatījumi
- Attēls veiksmīgi augšupielādēts uz GooglePhotos!
+ Attēls veiksmīgi augšupielādēts uz Picasa!
- Kļūda augšuplādējot uz GooglePhotos:
+ Kļūda augšuplādējot uz Picasa:
Attēla formāts
- Savienojos ar GooglePhotos. Lūdzu uzgaidiet...
+ Savienojos ar Picasa. Lūdzu uzgaidiet...
diff --git a/GreenshotPicasaPlugin/Languages/language_picasaplugin-pl-PL.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-pl-PL.xml
new file mode 100644
index 000000000..c4be12b84
--- /dev/null
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-pl-PL.xml
@@ -0,0 +1,14 @@
+
+
+
+ Trwa komunikacja z Picasa. Proszę czekać...
+ Konfiguruj Picasa
+ Po wysłaniu
+ Link do schowka
+ Format obrazów
+ Ustawienia Picasa
+ Wystąpił błąd przy wysyłaniu do Picasa:
+ Wyślij do Picasa
+ Wysyłanie obrazu do Picasa powiodło się!
+
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-pt-PT.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-pt-PT.xml
similarity index 73%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-pt-PT.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-pt-PT.xml
index c3194dad0..4649e8005 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-pt-PT.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-pt-PT.xml
@@ -8,25 +8,25 @@
Após enviar
- Configurar o GooglePhotos
+ Configurar o Picasa
- enviar para o GooglePhotos
+ enviar para o Picasa
- Definições GooglePhotos
+ Definições Picasa
- Imagem enviada com êxito para o GooglePhotos!
+ Imagem enviada com êxito para o Picasa!
- Ocorreu um erro ao enviar para o GooglePhotos:
+ Ocorreu um erro ao enviar para o Picasa:
Formato da imagem
- A comunicar com o GooglePhotos. Por favor aguarde...
+ A comunicar com o Picasa. Por favor aguarde...
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-ru-RU.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-ru-RU.xml
similarity index 61%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-ru-RU.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-ru-RU.xml
index c7aa0317d..7a273bce7 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-ru-RU.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-ru-RU.xml
@@ -1,14 +1,14 @@
-
-
-
- Обмен данными с GooglePhotos. Подождите...
- Настройка GooglePhotos
- После загрузки
- Ссылки в буфер обмена
- Формат изображения
- Настройки GooglePhotos
- Произошла ошибка при загрузке на GooglePhotos:
- Загрузить на GooglePhotos
- Изображение успешно загружено на GooglePhotos!
-
+
+
+
+ Обмен данными с Picasa. Подождите...
+ Настройка Picasa
+ После загрузки
+ Ссылки в буфер обмена
+ Формат изображения
+ Настройки Picasa
+ Произошла ошибка при загрузке на Picasa:
+ Загрузить на Picasa
+ Изображение успешно загружено на Picasa!
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-sr-RS.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-sr-RS.xml
similarity index 98%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-sr-RS.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-sr-RS.xml
index 88c045edd..f43494f01 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-sr-RS.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-sr-RS.xml
@@ -1,14 +1,14 @@
-
-
-
- Комуницирам с Пикасом. Сачекајте…
- Поставке Пикасе
- Након отпремања:
- Веза ка остави
- Формат слике:
- Поставке Пикасе
- Дошло је до грешке при отпремању на Пикасу:
- Отпреми на Пикасу
- Слика је успешно отпремљена на Пикасу.
-
+
+
+
+ Комуницирам с Пикасом. Сачекајте…
+ Поставке Пикасе
+ Након отпремања:
+ Веза ка остави
+ Формат слике:
+ Поставке Пикасе
+ Дошло је до грешке при отпремању на Пикасу:
+ Отпреми на Пикасу
+ Слика је успешно отпремљена на Пикасу.
+
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-sv-SE.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-sv-SE.xml
similarity index 73%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-sv-SE.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-sv-SE.xml
index 86a0aec4c..d3469d802 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-sv-SE.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-sv-SE.xml
@@ -8,25 +8,25 @@
Vid uppladdning
- Konfigurera GooglePhotos
+ Konfigurera Picasa
- Ladda upp till GooglePhotos
+ Ladda upp till Picasa
- GooglePhotos-inställningar
+ Picasa-inställningar
- Skärmdumpen laddades upp till GooglePhotos!
+ Skärmdumpen laddades upp till Picasa!
- Ett fel uppstod vid uppladdning till GooglePhotos:
+ Ett fel uppstod vid uppladdning till Picasa:
Bildformat
- Kommunicerar med GooglePhotos. Vänta...
+ Kommunicerar med Picasa. Vänta...
\ No newline at end of file
diff --git a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-uk-UA.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-uk-UA.xml
similarity index 53%
rename from src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-uk-UA.xml
rename to GreenshotPicasaPlugin/Languages/language_picasaplugin-uk-UA.xml
index 9be4ab5b6..c1ecd0a4c 100644
--- a/src/Greenshot.Plugin.GooglePhotos/Languages/language_googlephotos-uk-UA.xml
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-uk-UA.xml
@@ -1,14 +1,14 @@
-
-
-
- Посилання в буфер обміну
- Після вивантаження
- Налаштувати GooglePhotos
- Вивантажити на GooglePhotos
- Параметри GooglePhotos
- Зображення вдало вивантажено на GooglePhotos!
- Відбулась помилка під час вивантаження на GooglePhotos:
- Формат зображення
- З’єднання з GooglePhotos. Будь ласка, зачекайте...
-
-
+
+
+
+ Посилання в буфер обміну
+ Після вивантаження
+ Налаштувати Picasa
+ Вивантажити на Picasa
+ Параметри Picasa
+ Зображення вдало вивантажено на Picasa!
+ Відбулась помилка під час вивантаження на Picasa:
+ Формат зображення
+ З’єднання з Picasa. Будь ласка, зачекайте...
+
+
diff --git a/GreenshotPicasaPlugin/Languages/language_picasaplugin-zh-CN.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-zh-CN.xml
new file mode 100644
index 000000000..38601fc93
--- /dev/null
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-zh-CN.xml
@@ -0,0 +1,14 @@
+
+
+
+ 正在连接到Picasa。请稍后...
+ 配置 Picasa
+ 上传之后
+ 复制链接到剪贴板
+ 图片格式
+ Picasa设置
+ 上传到Picasa时发生错误:
+ 上传到Picasa
+ 图片已成功上传到了Picasa!
+
+
\ No newline at end of file
diff --git a/GreenshotPicasaPlugin/Languages/language_picasaplugin-zh-TW.xml b/GreenshotPicasaPlugin/Languages/language_picasaplugin-zh-TW.xml
new file mode 100644
index 000000000..a818bc748
--- /dev/null
+++ b/GreenshotPicasaPlugin/Languages/language_picasaplugin-zh-TW.xml
@@ -0,0 +1,14 @@
+
+
+
+ 正在與 Picasa 通訊,請稍候...
+ 組態 Picasa
+ 上傳後
+ 連結到剪貼簿
+ 圖片格式
+ Picasa 設定
+ 上傳到 Picasa 時發生錯誤:
+ 上傳到 Picasa
+ 上傳圖片到 Picasa 成功!
+
+
\ No newline at end of file
diff --git a/GreenshotPicasaPlugin/Picasa.png b/GreenshotPicasaPlugin/Picasa.png
new file mode 100644
index 000000000..97b70cf86
Binary files /dev/null and b/GreenshotPicasaPlugin/Picasa.png differ
diff --git a/GreenshotPicasaPlugin/PicasaConfiguration.cs b/GreenshotPicasaPlugin/PicasaConfiguration.cs
new file mode 100644
index 000000000..eb8eb7100
--- /dev/null
+++ b/GreenshotPicasaPlugin/PicasaConfiguration.cs
@@ -0,0 +1,92 @@
+/*
+ * A Picasa Plugin for Greenshot
+ * Copyright (C) 2011 Francis Noel
+ *
+ * For more information see: http://getgreenshot.org/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System.Windows.Forms;
+using GreenshotPlugin.Core;
+using System;
+using GreenshotPlugin.IniFile;
+
+namespace GreenshotPicasaPlugin {
+ ///
+ /// Description of PicasaConfiguration.
+ ///
+ [IniSection("Picasa", Description = "Greenshot Picasa Plugin configuration")]
+ public class PicasaConfiguration : IniSection {
+ [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
+ public OutputFormat UploadFormat { get; set; }
+
+ [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
+ public int UploadJpegQuality { get; set; }
+
+ [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Picasa link to clipboard.", DefaultValue = "true")]
+ public bool AfterUploadLinkToClipBoard { get; set; }
+ [IniProperty("AddFilename", Description = "Is the filename passed on to Picasa", DefaultValue = "False")]
+ public bool AddFilename {
+ get;
+ set;
+ }
+
+ [IniProperty("UploadUser", Description = "The picasa user to upload to", DefaultValue = "default")]
+ public string UploadUser {
+ get;
+ set;
+ }
+
+ [IniProperty("UploadAlbum", Description = "The picasa album to upload to", DefaultValue = "default")]
+ public string UploadAlbum {
+ get;
+ set;
+ }
+
+ [IniProperty("RefreshToken", Description = "Picasa authorization refresh Token", Encrypted = true)]
+ public string RefreshToken {
+ get;
+ set;
+ }
+
+ ///
+ /// Not stored
+ ///
+ public string AccessToken {
+ get;
+ set;
+ }
+
+ ///
+ /// Not stored
+ ///
+ public DateTimeOffset AccessTokenExpires {
+ get;
+ set;
+ }
+
+ ///
+ /// A form for token
+ ///
+ /// bool true if OK was pressed, false if cancel
+ public bool ShowConfigDialog() {
+ DialogResult result = new SettingsForm().ShowDialog();
+ if (result == DialogResult.OK) {
+ return true;
+ }
+ return false;
+ }
+
+ }
+}
diff --git a/GreenshotPicasaPlugin/PicasaDestination.cs b/GreenshotPicasaPlugin/PicasaDestination.cs
new file mode 100644
index 000000000..4107ecd48
--- /dev/null
+++ b/GreenshotPicasaPlugin/PicasaDestination.cs
@@ -0,0 +1,54 @@
+/*
+ * A Picasa Plugin for Greenshot
+ * Copyright (C) 2011 Francis Noel
+ *
+ * For more information see: http://getgreenshot.org/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System.ComponentModel;
+using System.Drawing;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces;
+
+namespace GreenshotPicasaPlugin {
+ public class PicasaDestination : AbstractDestination {
+ private readonly PicasaPlugin _plugin;
+ public PicasaDestination(PicasaPlugin plugin) {
+ _plugin = plugin;
+ }
+
+ public override string Designation => "Picasa";
+
+ public override string Description => Language.GetString("picasa", LangKey.upload_menu_item);
+
+ public override Image DisplayIcon {
+ get {
+ ComponentResourceManager resources = new ComponentResourceManager(typeof(PicasaPlugin));
+ return (Image)resources.GetObject("Picasa");
+ }
+ }
+
+ public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
+ ExportInformation exportInformation = new ExportInformation(Designation, Description);
+ bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl);
+ if (uploaded) {
+ exportInformation.ExportMade = true;
+ exportInformation.Uri = uploadUrl;
+ }
+ ProcessExport(exportInformation, surface);
+ return exportInformation;
+ }
+ }
+}
diff --git a/GreenshotPicasaPlugin/PicasaPlugin.cs b/GreenshotPicasaPlugin/PicasaPlugin.cs
new file mode 100644
index 000000000..a8f312484
--- /dev/null
+++ b/GreenshotPicasaPlugin/PicasaPlugin.cs
@@ -0,0 +1,125 @@
+/*
+ * A Picasa Plugin for Greenshot
+ * Copyright (C) 2011 Francis Noel
+ *
+ * For more information see: http://getgreenshot.org/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+using GreenshotPlugin.Controls;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotPicasaPlugin {
+ ///
+ /// This is the Picasa base code
+ ///
+ [Plugin("Picasa", true)]
+ public class PicasaPlugin : IGreenshotPlugin {
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PicasaPlugin));
+ private static PicasaConfiguration _config;
+ private ComponentResourceManager _resources;
+ private ToolStripMenuItem _itemPlugInRoot;
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected void Dispose(bool disposing) {
+ if (disposing) {
+ if (_itemPlugInRoot != null) {
+ _itemPlugInRoot.Dispose();
+ _itemPlugInRoot = null;
+ }
+ }
+ }
+
+ ///
+ /// Implementation of the IGreenshotPlugin.Initialize
+ ///
+ public bool Initialize() {
+ SimpleServiceProvider.Current.AddService(new PicasaDestination(this));
+
+ // Get configuration
+ _config = IniConfig.GetIniSection();
+ _resources = new ComponentResourceManager(typeof(PicasaPlugin));
+
+ _itemPlugInRoot = new ToolStripMenuItem
+ {
+ Text = Language.GetString("picasa", LangKey.Configure),
+ Image = (Image) _resources.GetObject("Picasa")
+ };
+ _itemPlugInRoot.Click += ConfigMenuClick;
+ PluginUtils.AddToContextMenu(_itemPlugInRoot);
+ Language.LanguageChanged += OnLanguageChanged;
+ return true;
+ }
+
+ public void OnLanguageChanged(object sender, EventArgs e) {
+ if (_itemPlugInRoot != null) {
+ _itemPlugInRoot.Text = Language.GetString("picasa", LangKey.Configure);
+ }
+ }
+
+ public void Shutdown() {
+ Log.Debug("Picasa Plugin shutdown.");
+ Language.LanguageChanged -= OnLanguageChanged;
+ //host.OnImageEditorOpen -= new OnImageEditorOpenHandler(ImageEditorOpened);
+ }
+
+ ///
+ /// Implementation of the IPlugin.Configure
+ ///
+ public void Configure() {
+ _config.ShowConfigDialog();
+ }
+
+ public void ConfigMenuClick(object sender, EventArgs eventArgs) {
+ Configure();
+ }
+
+ public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) {
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality);
+ try {
+ string url = null;
+ new PleaseWaitForm().ShowAndWait("Picasa", Language.GetString("picasa", LangKey.communication_wait),
+ delegate
+ {
+ string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
+ url = PicasaUtils.UploadToPicasa(surfaceToUpload, outputSettings, captureDetails.Title, filename);
+ }
+ );
+ uploadUrl = url;
+
+ if (uploadUrl != null && _config.AfterUploadLinkToClipBoard) {
+ ClipboardHelper.SetClipboardData(uploadUrl);
+ }
+ return true;
+ } catch (Exception e) {
+ Log.Error("Error uploading.", e);
+ MessageBox.Show(Language.GetString("picasa", LangKey.upload_failure) + " " + e.Message);
+ }
+ uploadUrl = null;
+ return false;
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosPlugin.resx b/GreenshotPicasaPlugin/PicasaPlugin.resx
similarity index 96%
rename from src/Greenshot.Plugin.GooglePhotos/GooglePhotosPlugin.resx
rename to GreenshotPicasaPlugin/PicasaPlugin.resx
index 240e4a53e..de5045226 100644
--- a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosPlugin.resx
+++ b/GreenshotPicasaPlugin/PicasaPlugin.resx
@@ -118,7 +118,7 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- GooglePhotos.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ picasa.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
\ No newline at end of file
diff --git a/GreenshotPicasaPlugin/PicasaUtils.cs b/GreenshotPicasaPlugin/PicasaUtils.cs
new file mode 100644
index 000000000..eb2b2be2e
--- /dev/null
+++ b/GreenshotPicasaPlugin/PicasaUtils.cs
@@ -0,0 +1,119 @@
+/*
+ * A Picasa Plugin for Greenshot
+ * Copyright (C) 2011 Francis Noel
+ *
+ * For more information see: http://getgreenshot.org/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using GreenshotPlugin.Core;
+using System;
+using System.Xml;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotPicasaPlugin {
+ ///
+ /// Description of PicasaUtils.
+ ///
+ public static class PicasaUtils {
+ private const string PicasaScope = "https://picasaweb.google.com/data/";
+ private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PicasaUtils));
+ private static readonly PicasaConfiguration Config = IniConfig.GetIniSection();
+ private const string AuthUrl = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}&scope=" + PicasaScope;
+ private const string TokenUrl = "https://www.googleapis.com/oauth2/v3/token";
+ private const string UploadUrl = "https://picasaweb.google.com/data/feed/api/user/{0}/albumid/{1}";
+
+ ///
+ /// Do the actual upload to Picasa
+ ///
+ /// Image to upload
+ ///
+ ///
+ ///
+ /// PicasaResponse
+ public static string UploadToPicasa(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) {
+ // Fill the OAuth2Settings
+ var settings = new OAuth2Settings
+ {
+ AuthUrlPattern = AuthUrl,
+ TokenUrl = TokenUrl,
+ CloudServiceName = "Picasa",
+ ClientId = PicasaCredentials.ClientId,
+ ClientSecret = PicasaCredentials.ClientSecret,
+ AuthorizeMode = OAuth2AuthorizeMode.LocalServer,
+ RefreshToken = Config.RefreshToken,
+ AccessToken = Config.AccessToken,
+ AccessTokenExpires = Config.AccessTokenExpires
+ };
+
+ // Copy the settings from the config, which is kept in memory and on the disk
+
+ try {
+ var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, string.Format(UploadUrl, Config.UploadUser, Config.UploadAlbum), settings);
+ if (Config.AddFilename) {
+ webRequest.Headers.Add("Slug", NetworkHelper.EscapeDataString(filename));
+ }
+ SurfaceContainer container = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
+ container.Upload(webRequest);
+
+ string response = NetworkHelper.GetResponseAsString(webRequest);
+
+ return ParseResponse(response);
+ } finally {
+ // Copy the settings back to the config, so they are stored.
+ Config.RefreshToken = settings.RefreshToken;
+ Config.AccessToken = settings.AccessToken;
+ Config.AccessTokenExpires = settings.AccessTokenExpires;
+ Config.IsDirty = true;
+ IniConfig.Save();
+ }
+ }
+
+ ///
+ /// Parse the upload URL from the response
+ ///
+ ///
+ ///
+ public static string ParseResponse(string response) {
+ if (response == null) {
+ return null;
+ }
+ try {
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(response);
+ XmlNodeList nodes = doc.GetElementsByTagName("link", "*");
+ if(nodes.Count > 0) {
+ string url = null;
+ foreach(XmlNode node in nodes) {
+ if (node.Attributes != null) {
+ url = node.Attributes["href"].Value;
+ string rel = node.Attributes["rel"].Value;
+ // Pictures with rel="http://schemas.google.com/photos/2007#canonical" are the direct link
+ if (rel != null && rel.EndsWith("canonical")) {
+ break;
+ }
+ }
+ }
+ return url;
+ }
+ } catch(Exception e) {
+ Log.ErrorFormat("Could not parse Picasa response due to error {0}, response was: {1}", e.Message, response);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs b/GreenshotPicasaPlugin/Properties/AssemblyInfo.cs
similarity index 75%
rename from src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs
rename to GreenshotPicasaPlugin/Properties/AssemblyInfo.cs
index 44f8800ce..35cc458c3 100644
--- a/src/Greenshot.Plugin.Jira/Properties/AssemblyInfo.cs
+++ b/GreenshotPicasaPlugin/Properties/AssemblyInfo.cs
@@ -1,8 +1,8 @@
/*
* Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel
*
- * For more information see: https://getgreenshot.org/
+ * For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
@@ -16,19 +16,17 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
using System.Reflection;
using System.Runtime.InteropServices;
-using Greenshot.Base.Interfaces.Plugin;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyDescription("A plugin to upload images to Jira")]
-[assembly: AssemblyPluginIdentifier("Jira Plugin")]
+[assembly: AssemblyDescription("A plugin to upload images to Picasa")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
-[assembly: ComVisible(false)]
\ No newline at end of file
+[assembly: ComVisible(false)]
diff --git a/src/Greenshot.Plugin.GooglePhotos/README b/GreenshotPicasaPlugin/README
similarity index 100%
rename from src/Greenshot.Plugin.GooglePhotos/README
rename to GreenshotPicasaPlugin/README
diff --git a/GreenshotPlugin/Controls/AnimatingForm.cs b/GreenshotPlugin/Controls/AnimatingForm.cs
new file mode 100644
index 000000000..91f4762f0
--- /dev/null
+++ b/GreenshotPlugin/Controls/AnimatingForm.cs
@@ -0,0 +1,128 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Windows.Forms;
+using GreenshotPlugin.UnmanagedHelpers;
+using GreenshotPlugin.UnmanagedHelpers.Enums;
+using log4net;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// Extend this Form to have the possibility for animations on your form
+ ///
+ public class AnimatingForm : GreenshotForm {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(AnimatingForm));
+ private const int DEFAULT_VREFRESH = 60;
+ private int _vRefresh;
+ private Timer _timer;
+
+ ///
+ /// This flag specifies if any animation is used
+ ///
+ protected bool EnableAnimation {
+ get;
+ set;
+ }
+
+ ///
+ /// Vertical Refresh Rate
+ ///
+ protected int VRefresh {
+ get {
+ if (_vRefresh == 0)
+ {
+ // get te hDC of the desktop to get the VREFRESH
+ using SafeWindowDcHandle desktopHandle = SafeWindowDcHandle.FromDesktop();
+ _vRefresh = GDI32.GetDeviceCaps(desktopHandle, DeviceCaps.VREFRESH);
+ }
+ // A vertical refresh rate value of 0 or 1 represents the display hardware's default refresh rate.
+ // As there is currently no know way to get the default, we guess it.
+ if (_vRefresh <= 1) {
+ _vRefresh = DEFAULT_VREFRESH;
+ }
+ return _vRefresh;
+ }
+ }
+
+ ///
+ /// Check if we are in a Terminal Server session OR need to optimize for RDP / remote desktop connections
+ ///
+ protected bool IsTerminalServerSession => !coreConfiguration.DisableRDPOptimizing && (coreConfiguration.OptimizeForRDP || SystemInformation.TerminalServerSession);
+
+ ///
+ /// Calculate the amount of frames that an animation takes
+ ///
+ ///
+ /// Number of frames, 1 if in Terminal Server Session
+ protected int FramesForMillis(int milliseconds) {
+ // If we are in a Terminal Server Session we return 1
+ if (IsTerminalServerSession) {
+ return 1;
+ }
+ return milliseconds / VRefresh;
+ }
+
+ ///
+ /// Initialize the animation
+ ///
+ protected AnimatingForm() {
+ Load += delegate {
+ if (!EnableAnimation)
+ {
+ return;
+ }
+ _timer = new Timer
+ {
+ Interval = 1000/VRefresh
+ };
+ _timer.Tick += timer_Tick;
+ _timer.Start();
+ };
+
+ // Unregister at close
+ FormClosing += delegate
+ {
+ _timer?.Stop();
+ };
+ }
+
+ ///
+ /// The tick handler initiates the animation.
+ ///
+ ///
+ ///
+ private void timer_Tick(object sender, EventArgs e) {
+ try {
+ Animate();
+ } catch (Exception ex) {
+ Log.Warn("An exception occured while animating:", ex);
+ }
+ }
+
+ ///
+ /// This method will be called every frame, so implement your animation/redraw logic here.
+ ///
+ protected virtual void Animate() {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Greenshot.Base/Controls/BackgroundForm.Designer.cs b/GreenshotPlugin/Controls/BackgroundForm.Designer.cs
similarity index 86%
rename from src/Greenshot.Base/Controls/BackgroundForm.Designer.cs
rename to GreenshotPlugin/Controls/BackgroundForm.Designer.cs
index 331dedd48..bef6439b5 100644
--- a/src/Greenshot.Base/Controls/BackgroundForm.Designer.cs
+++ b/GreenshotPlugin/Controls/BackgroundForm.Designer.cs
@@ -1,96 +1,98 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: https://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-namespace Greenshot.Base.Controls
-{
- partial class BackgroundForm
- {
- ///
- /// Designer variable used to keep track of non-visual components.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Disposes resources used by the form.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing) {
- if (components != null) {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
-
- ///
- /// This method is required for Windows Forms designer support.
- /// Do not change the method contents inside the source code editor. The Forms designer might
- /// not be able to load this method if it was changed manually.
- ///
- private void InitializeComponent()
- {
- this.components = new System.ComponentModel.Container();
- this.label_pleasewait = new System.Windows.Forms.Label();
- this.timer_checkforclose = new System.Windows.Forms.Timer(this.components);
- this.SuspendLayout();
- //
- // label_pleasewait
- //
- this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill;
- this.label_pleasewait.Location = new System.Drawing.Point(0, 0);
- this.label_pleasewait.Name = "label_pleasewait";
- this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10);
- this.label_pleasewait.Size = new System.Drawing.Size(90, 33);
- this.label_pleasewait.TabIndex = 0;
- this.label_pleasewait.Text = "Please wait...";
- this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
- this.label_pleasewait.UseWaitCursor = true;
- //
- // timer_checkforclose
- //
- this.timer_checkforclose.Interval = 200;
- this.timer_checkforclose.Tick += new System.EventHandler(this.Timer_checkforcloseTick);
- //
- // BackgroundForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(169, 52);
- this.ControlBox = true;
- this.Controls.Add(this.label_pleasewait);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "BackgroundForm";
- this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Greenshot";
- this.TopMost = true;
- this.UseWaitCursor = true;
- this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BackgroundFormFormClosing);
- this.ResumeLayout(false);
- this.PerformLayout();
- }
- private System.Windows.Forms.Timer timer_checkforclose;
- private System.Windows.Forms.Label label_pleasewait;
- }
-}
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace GreenshotPlugin.Controls
+{
+ partial class BackgroundForm
+ {
+ ///
+ /// Designer variable used to keep track of non-visual components.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Disposes resources used by the form.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing) {
+ if (components != null) {
+ components.Dispose();
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// This method is required for Windows Forms designer support.
+ /// Do not change the method contents inside the source code editor. The Forms designer might
+ /// not be able to load this method if it was changed manually.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.label_pleasewait = new System.Windows.Forms.Label();
+ this.timer_checkforclose = new System.Windows.Forms.Timer(this.components);
+ this.SuspendLayout();
+ //
+ // label_pleasewait
+ //
+ this.label_pleasewait.AutoSize = true;
+ this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.label_pleasewait.Location = new System.Drawing.Point(0, 0);
+ this.label_pleasewait.Name = "label_pleasewait";
+ this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10);
+ this.label_pleasewait.Size = new System.Drawing.Size(90, 33);
+ this.label_pleasewait.TabIndex = 0;
+ this.label_pleasewait.Text = "Please wait...";
+ this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.label_pleasewait.UseWaitCursor = true;
+ //
+ // timer_checkforclose
+ //
+ this.timer_checkforclose.Interval = 200;
+ this.timer_checkforclose.Tick += new System.EventHandler(this.Timer_checkforcloseTick);
+ //
+ // BackgroundForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoSize = true;
+ this.ClientSize = new System.Drawing.Size(169, 52);
+ this.ControlBox = true;
+ this.Controls.Add(this.label_pleasewait);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "BackgroundForm";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Greenshot";
+ this.TopMost = true;
+ this.UseWaitCursor = true;
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BackgroundFormFormClosing);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+ }
+ private System.Windows.Forms.Timer timer_checkforclose;
+ private System.Windows.Forms.Label label_pleasewait;
+ }
+}
diff --git a/GreenshotPlugin/Controls/BackgroundForm.cs b/GreenshotPlugin/Controls/BackgroundForm.cs
new file mode 100644
index 000000000..5d90c6920
--- /dev/null
+++ b/GreenshotPlugin/Controls/BackgroundForm.cs
@@ -0,0 +1,100 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Drawing;
+using System.Threading;
+using System.Windows.Forms;
+using GreenshotPlugin.Core;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// Description of PleaseWaitForm.
+ ///
+ public sealed partial class BackgroundForm : Form {
+ private volatile bool _shouldClose;
+
+ private void BackgroundShowDialog() {
+ ShowDialog();
+ }
+
+ public static BackgroundForm ShowAndWait(string title, string text) {
+ BackgroundForm backgroundForm = new BackgroundForm(title, text);
+ // Show form in background thread
+ Thread backgroundTask = new Thread (backgroundForm.BackgroundShowDialog);
+ backgroundForm.Name = "Background form";
+ backgroundTask.IsBackground = true;
+ backgroundTask.SetApartmentState(ApartmentState.STA);
+ backgroundTask.Start();
+ return backgroundForm;
+ }
+
+ public BackgroundForm(string title, string text){
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+ Icon = GreenshotResources.GetGreenshotIcon();
+ _shouldClose = false;
+ Text = title;
+ label_pleasewait.Text = text;
+ FormClosing += PreventFormClose;
+ timer_checkforclose.Start();
+ }
+
+ // Can be used instead of ShowDialog
+ public new void Show() {
+ base.Show();
+ bool positioned = false;
+ foreach(Screen screen in Screen.AllScreens) {
+ if (screen.Bounds.Contains(Cursor.Position)) {
+ positioned = true;
+ Location = new Point(screen.Bounds.X + (screen.Bounds.Width / 2) - (Width / 2), screen.Bounds.Y + (screen.Bounds.Height / 2) - (Height / 2));
+ break;
+ }
+ }
+ if (!positioned) {
+ Location = new Point(Cursor.Position.X - Width / 2, Cursor.Position.Y - Height / 2);
+ }
+ }
+
+ private void PreventFormClose(object sender, FormClosingEventArgs e) {
+ if(!_shouldClose) {
+ e.Cancel = true;
+ }
+ }
+
+ private void Timer_checkforcloseTick(object sender, EventArgs e) {
+ if (_shouldClose) {
+ timer_checkforclose.Stop();
+ BeginInvoke(new EventHandler( delegate {Close();}));
+ }
+ }
+
+ public void CloseDialog() {
+ _shouldClose = true;
+ Application.DoEvents();
+ }
+
+ private void BackgroundFormFormClosing(object sender, FormClosingEventArgs e) {
+ timer_checkforclose.Stop();
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/ExtendedWebBrowser.cs b/GreenshotPlugin/Controls/ExtendedWebBrowser.cs
new file mode 100644
index 000000000..78a793866
--- /dev/null
+++ b/GreenshotPlugin/Controls/ExtendedWebBrowser.cs
@@ -0,0 +1,58 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Windows.Forms;
+using GreenshotPlugin.Interop;
+
+namespace GreenshotPlugin.Controls {
+ public class ExtendedWebBrowser : WebBrowser {
+ protected class ExtendedWebBrowserSite : WebBrowserSite, IOleCommandTarget {
+ private const int OLECMDID_SHOWSCRIPTERROR = 40;
+
+ private static readonly Guid CGID_DocHostCommandHandler = new Guid("F38BC242-B950-11D1-8918-00C04FC2C836");
+
+ private const int S_OK = 0;
+ private const int OLECMDERR_E_NOTSUPPORTED = (-2147221248);
+
+ public ExtendedWebBrowserSite(WebBrowser wb) : base(wb) {
+ }
+
+ public int QueryStatus(Guid pguidCmdGroup, int cCmds, IntPtr prgCmds, IntPtr pCmdText) {
+ return OLECMDERR_E_NOTSUPPORTED;
+ }
+
+ public int Exec(Guid pguidCmdGroup, int nCmdID, int nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) {
+ if (pguidCmdGroup == CGID_DocHostCommandHandler) {
+ if (nCmdID == OLECMDID_SHOWSCRIPTERROR) {
+ // do not need to alter pvaOut as the docs says, enough to return S_OK here
+ return S_OK;
+ }
+ }
+
+ return OLECMDERR_E_NOTSUPPORTED;
+ }
+ }
+
+ protected override WebBrowserSiteBase CreateWebBrowserSiteBase() {
+ return new ExtendedWebBrowserSite(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Greenshot.Base/Controls/FormWithoutActivation.cs b/GreenshotPlugin/Controls/FormWithoutActivation.cs
similarity index 72%
rename from src/Greenshot.Base/Controls/FormWithoutActivation.cs
rename to GreenshotPlugin/Controls/FormWithoutActivation.cs
index 0a69bc043..3fd3f8e30 100644
--- a/src/Greenshot.Base/Controls/FormWithoutActivation.cs
+++ b/GreenshotPlugin/Controls/FormWithoutActivation.cs
@@ -1,36 +1,33 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: https://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.Windows.Forms;
-
-namespace Greenshot.Base.Controls
-{
- ///
- /// FormWithoutActivation is exactly like a normal form, but doesn't activate (steal focus)
- ///
- public class FormWithoutActivation : Form
- {
- protected override bool ShowWithoutActivation
- {
- get { return true; }
- }
- }
-}
\ No newline at end of file
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// FormWithoutActivation is exactly like a normal form, but doesn't activate (steal focus)
+ ///
+ public class FormWithoutActivation : Form {
+ protected override bool ShowWithoutActivation {
+ get { return true; }
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotButton.cs b/GreenshotPlugin/Controls/GreenshotButton.cs
new file mode 100644
index 000000000..2cbee2916
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotButton.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotButton : Button, IGreenshotLanguageBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotCheckBox.cs b/GreenshotPlugin/Controls/GreenshotCheckBox.cs
new file mode 100644
index 000000000..53300d5d7
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotCheckBox.cs
@@ -0,0 +1,45 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// Description of GreenshotCheckbox.
+ ///
+ public class GreenshotCheckBox : CheckBox, IGreenshotLanguageBindable, IGreenshotConfigBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+
+ [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")]
+ public string SectionName { get; set; } = "Core";
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
+ public string PropertyName {
+ get;
+ set;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Controls/GreenshotColumnSorter.cs b/GreenshotPlugin/Controls/GreenshotColumnSorter.cs
new file mode 100644
index 000000000..379d53883
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotColumnSorter.cs
@@ -0,0 +1,118 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System.Collections;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// This class is an implementation of the 'IComparer' interface.
+ ///
+ public class GreenshotColumnSorter : IComparer {
+ ///
+ /// Specifies the column to be sorted
+ ///
+ private int _columnToSort;
+ ///
+ /// Specifies the order in which to sort (i.e. 'Ascending').
+ ///
+ private SortOrder _orderOfSort;
+ ///
+ /// Case insensitive comparer object
+ ///
+ private readonly CaseInsensitiveComparer _objectCompare;
+
+ ///
+ /// Class constructor. Initializes various elements
+ ///
+ public GreenshotColumnSorter() {
+ // Initialize the column to '0'
+ _columnToSort = 0;
+
+ // Initialize the sort order to 'none'
+ _orderOfSort = SortOrder.None;
+
+ // Initialize the CaseInsensitiveComparer object
+ _objectCompare = new CaseInsensitiveComparer();
+ }
+
+ ///
+ /// This method is inherited from the IComparer interface. It compares the two objects passed using a case insensitive comparison.
+ ///
+ /// First object to be compared
+ /// Second object to be compared
+ /// The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y'
+ public int Compare(object x, object y) {
+ if (x == null && y == null) {
+ return 0;
+ }
+ if (x == null) {
+ return -1;
+ }
+ if (y == null) {
+ return 1;
+ }
+ // Cast the objects to be compared to ListViewItem objects
+ var listviewX = (ListViewItem)x;
+ var listviewY = (ListViewItem)y;
+
+ // Compare the two items
+ var compareResult = _objectCompare.Compare(listviewX.SubItems[_columnToSort].Text, listviewY.SubItems[_columnToSort].Text);
+
+ // Calculate correct return value based on object comparison
+ if (_orderOfSort == SortOrder.Ascending) {
+ // Ascending sort is selected, return normal result of compare operation
+ return compareResult;
+ }
+ if (_orderOfSort == SortOrder.Descending) {
+ // Descending sort is selected, return negative result of compare operation
+ return -compareResult;
+ }
+ // Return '0' to indicate they are equal
+ return 0;
+ }
+
+ ///
+ /// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0').
+ ///
+ public int SortColumn {
+ set {
+ _columnToSort = value;
+ }
+ get {
+ return _columnToSort;
+ }
+ }
+
+ ///
+ /// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
+ ///
+ public SortOrder Order {
+ set {
+ _orderOfSort = value;
+ }
+ get {
+ return _orderOfSort;
+ }
+ }
+ }
+}
+
+
diff --git a/GreenshotPlugin/Controls/GreenshotComboBox.cs b/GreenshotPlugin/Controls/GreenshotComboBox.cs
new file mode 100644
index 000000000..e5245b5a2
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotComboBox.cs
@@ -0,0 +1,104 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.ComponentModel;
+using System.Windows.Forms;
+using GreenshotPlugin.Core;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotComboBox : ComboBox, IGreenshotConfigBindable {
+ private Type _enumType;
+ private Enum _selectedEnum;
+
+ [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")]
+ public string SectionName { get; set; } = "Core";
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
+ public string PropertyName {
+ get;
+ set;
+ }
+
+ public GreenshotComboBox() {
+ SelectedIndexChanged += delegate {
+ StoreSelectedEnum();
+ };
+ }
+
+ public void SetValue(Enum currentValue) {
+ if (currentValue != null) {
+ _selectedEnum = currentValue;
+ SelectedItem = Language.Translate(currentValue);
+ }
+ }
+
+ ///
+ /// This is a method to popululate the ComboBox
+ /// with the items from the enumeration
+ ///
+ /// TEnum to populate with
+ public void Populate(Type enumType) {
+ // Store the enum-type, so we can work with it
+ _enumType = enumType;
+
+ var availableValues = Enum.GetValues(enumType);
+ Items.Clear();
+ foreach (var enumValue in availableValues) {
+ Items.Add(Language.Translate((Enum)enumValue));
+ }
+ }
+
+ ///
+ /// Store the selected value internally
+ ///
+ private void StoreSelectedEnum() {
+ string enumTypeName = _enumType.Name;
+ string selectedValue = SelectedItem as string;
+ var availableValues = Enum.GetValues(_enumType);
+ object returnValue = null;
+
+ try {
+ returnValue = Enum.Parse(_enumType, selectedValue);
+ } catch (Exception) {
+ // Ignore
+ }
+
+ foreach (Enum enumValue in availableValues) {
+ string enumKey = enumTypeName + "." + enumValue;
+ if (Language.HasKey(enumKey)) {
+ string translation = Language.GetString(enumTypeName + "." + enumValue);
+ if (translation.Equals(selectedValue)) {
+ returnValue = enumValue;
+ }
+ }
+ }
+ _selectedEnum = (Enum)returnValue;
+ }
+
+ ///
+ /// Get the selected enum value from the combobox, uses generics
+ ///
+ /// The enum value of the combobox
+ public Enum GetSelectedEnum() {
+ return _selectedEnum;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotForm.cs b/GreenshotPlugin/Controls/GreenshotForm.cs
new file mode 100644
index 000000000..a7926095f
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotForm.cs
@@ -0,0 +1,526 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using System.Reflection;
+using GreenshotPlugin.Core;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.IO;
+using GreenshotPlugin.IniFile;
+using log4net;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// This form is used for automatically binding the elements of the form to the language
+ ///
+ public class GreenshotForm : Form, IGreenshotLanguageBindable {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(GreenshotForm));
+ protected static CoreConfiguration coreConfiguration;
+ private static readonly IDictionary reflectionCache = new Dictionary();
+ private IComponentChangeService m_changeService;
+ private bool _isDesignModeLanguageSet;
+ private bool _applyLanguageManually;
+ private bool _storeFieldsManually;
+ private IDictionary _designTimeControls;
+ private IDictionary _designTimeToolStripItems;
+
+ static GreenshotForm() {
+ if (!IsInDesignMode) {
+ coreConfiguration = IniConfig.GetIniSection();
+ }
+ }
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+
+ ///
+ /// Used to check the designmode during a constructor
+ ///
+ ///
+ protected static bool IsInDesignMode {
+ get {
+ return (Application.ExecutablePath.IndexOf("devenv.exe", StringComparison.OrdinalIgnoreCase) > -1) || (Application.ExecutablePath.IndexOf("sharpdevelop.exe", StringComparison.OrdinalIgnoreCase) > -1 || (Application.ExecutablePath.IndexOf("wdexpress.exe", StringComparison.OrdinalIgnoreCase) > -1));
+ }
+ }
+
+ protected bool ManualLanguageApply {
+ get {
+ return _applyLanguageManually;
+ }
+ set {
+ _applyLanguageManually = value;
+ }
+ }
+
+ protected bool ManualStoreFields {
+ get {
+ return _storeFieldsManually;
+ }
+ set {
+ _storeFieldsManually = value;
+ }
+ }
+
+ ///
+ /// When this is set, the form will be brought to the foreground as soon as it is shown.
+ ///
+ protected bool ToFront {
+ get;
+ set;
+ }
+
+ ///
+ /// Code to initialize the language etc during design time
+ ///
+ protected void InitializeForDesigner() {
+ if (!DesignMode) return;
+ _designTimeControls = new Dictionary();
+ _designTimeToolStripItems = new Dictionary();
+ try {
+ ITypeResolutionService typeResService = GetService(typeof(ITypeResolutionService)) as ITypeResolutionService;
+
+ // Add a hard-path if you are using SharpDevelop
+ // Language.AddLanguageFilePath(@"C:\Greenshot\Greenshot\Languages");
+
+ // this "type"
+ Assembly currentAssembly = GetType().Assembly;
+ if (typeResService != null)
+ {
+ string assemblyPath = typeResService.GetPathOfAssembly(currentAssembly.GetName());
+ string assemblyDirectory = Path.GetDirectoryName(assemblyPath);
+ if (assemblyDirectory != null && !Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Greenshot\Languages\"))) {
+ Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Greenshot\Languages\"));
+ }
+ if (assemblyDirectory != null && !Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Languages\"))) {
+ Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Languages\"));
+ }
+ }
+ } catch (Exception ex) {
+ MessageBox.Show(ex.Message);
+ }
+ }
+
+ ///
+ /// This override is only for the design-time of the form
+ ///
+ ///
+ protected override void OnPaint(PaintEventArgs e) {
+ if (DesignMode) {
+ if (!_isDesignModeLanguageSet) {
+ _isDesignModeLanguageSet = true;
+ try {
+ ApplyLanguage();
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ }
+ }
+ base.OnPaint(e);
+ }
+
+ protected override void OnLoad(EventArgs e) {
+ // Every GreenshotForm should have it's default icon
+ // And it might not ne needed for a Tool Window, but still for the task manager / switcher it's important
+ Icon = GreenshotResources.GetGreenshotIcon();
+ if (!DesignMode) {
+ if (!_applyLanguageManually) {
+ ApplyLanguage();
+ }
+ FillFields();
+ base.OnLoad(e);
+ } else {
+ LOG.Info("OnLoad called from designer.");
+ InitializeForDesigner();
+ base.OnLoad(e);
+ ApplyLanguage();
+ }
+ }
+
+ ///
+ /// Make sure the form is visible, if this is wanted
+ ///
+ /// EventArgs
+ protected override void OnShown(EventArgs e) {
+ base.OnShown(e);
+ if (ToFront) {
+ WindowDetails.ToForeground(Handle);
+ }
+ }
+
+ ///
+ /// check if the form was closed with an OK, if so store the values in the GreenshotControls
+ ///
+ ///
+ protected override void OnClosed(EventArgs e) {
+ if (!DesignMode && !_storeFieldsManually) {
+ if (DialogResult == DialogResult.OK) {
+ LOG.Info("Form was closed with OK: storing field values.");
+ StoreFields();
+ }
+ }
+ base.OnClosed(e);
+ }
+
+ ///
+ /// This override allows the control to register event handlers for IComponentChangeService events
+ /// at the time the control is sited, which happens only in design mode.
+ ///
+ public override ISite Site {
+ get {
+ return base.Site;
+ }
+ set {
+ // Clear any component change event handlers.
+ ClearChangeNotifications();
+
+ // Set the new Site value.
+ base.Site = value;
+
+ m_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
+
+ // Register event handlers for component change events.
+ RegisterChangeNotifications();
+ }
+ }
+
+ private void ClearChangeNotifications() {
+ // The m_changeService value is null when not in design mode,
+ // as the IComponentChangeService is only available at design time.
+ m_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
+
+ // Clear our the component change events to prepare for re-siting.
+ if (m_changeService != null) {
+ m_changeService.ComponentChanged -= OnComponentChanged;
+ m_changeService.ComponentAdded -= OnComponentAdded;
+ }
+ }
+
+ private void RegisterChangeNotifications() {
+ // Register the event handlers for the IComponentChangeService events
+ if (m_changeService != null) {
+ m_changeService.ComponentChanged += OnComponentChanged;
+ m_changeService.ComponentAdded += OnComponentAdded;
+ }
+ }
+
+ ///
+ /// This method handles the OnComponentChanged event to display a notification.
+ ///
+ ///
+ ///
+ private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
+ {
+ if (((IComponent) ce.Component)?.Site == null || ce.Member == null) return;
+ if (!"LanguageKey".Equals(ce.Member.Name)) return;
+ if (ce.Component is Control control) {
+ LOG.InfoFormat("Changing LanguageKey for {0} to {1}", control.Name, ce.NewValue);
+ ApplyLanguage(control, (string)ce.NewValue);
+ } else {
+ if (ce.Component is ToolStripItem item) {
+ LOG.InfoFormat("Changing LanguageKey for {0} to {1}", item.Name, ce.NewValue);
+ ApplyLanguage(item, (string)ce.NewValue);
+ } else {
+ LOG.InfoFormat("Not possible to changing LanguageKey for {0} to {1}", ce.Component.GetType(), ce.NewValue);
+ }
+ }
+ }
+
+ private void OnComponentAdded(object sender, ComponentEventArgs ce) {
+ if (ce.Component?.Site == null) return;
+ if (ce.Component is Control control) {
+ if (!_designTimeControls.ContainsKey(control.Name)) {
+ _designTimeControls.Add(control.Name, control);
+ } else {
+ _designTimeControls[control.Name] = control;
+ }
+ }
+ else
+ {
+ if (ce.Component is ToolStripItem stripItem) {
+ ToolStripItem item = stripItem;
+ if (!_designTimeControls.ContainsKey(item.Name)) {
+ _designTimeToolStripItems.Add(item.Name, item);
+ } else {
+ _designTimeToolStripItems[item.Name] = item;
+ }
+ }
+ }
+ }
+
+ // Clean up any resources being used.
+ protected override void Dispose(bool disposing) {
+ if (disposing) {
+ ClearChangeNotifications();
+ }
+ base.Dispose(disposing);
+ }
+
+ protected void ApplyLanguage(ToolStripItem applyTo, string languageKey) {
+ string langString;
+ if (!string.IsNullOrEmpty(languageKey)) {
+ if (!Language.TryGetString(languageKey, out langString)) {
+ LOG.WarnFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name);
+ return;
+ }
+ applyTo.Text = langString;
+ } else {
+ // Fallback to control name!
+ if (Language.TryGetString(applyTo.Name, out langString)) {
+ applyTo.Text = langString;
+ return;
+ }
+ if (!DesignMode) {
+ LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name);
+ }
+ }
+ }
+
+ protected void ApplyLanguage(ToolStripItem applyTo) {
+ if (applyTo is IGreenshotLanguageBindable languageBindable) {
+ ApplyLanguage(applyTo, languageBindable.LanguageKey);
+ }
+ }
+
+ protected void ApplyLanguage(Control applyTo) {
+ if (!(applyTo is IGreenshotLanguageBindable languageBindable)) {
+ // check if it's a menu!
+ if (!(applyTo is ToolStrip toolStrip))
+ {
+ return;
+ }
+ foreach (ToolStripItem item in toolStrip.Items) {
+ ApplyLanguage(item);
+ }
+ return;
+ }
+
+ // Apply language text to the control
+ ApplyLanguage(applyTo, languageBindable.LanguageKey);
+
+ // Repopulate the combox boxes
+ if (applyTo is IGreenshotConfigBindable configBindable && applyTo is GreenshotComboBox comboxBox) {
+ if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) {
+ IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
+ if (section != null) {
+ // Only update the language, so get the actual value and than repopulate
+ Enum currentValue = comboxBox.GetSelectedEnum();
+ comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType);
+ comboxBox.SetValue(currentValue);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Helper method to cache the fieldinfo values, so we don't need to reflect all the time!
+ ///
+ ///
+ ///
+ private static FieldInfo[] GetCachedFields(Type typeToGetFieldsFor) {
+ if (!reflectionCache.TryGetValue(typeToGetFieldsFor, out var fields)) {
+ fields = typeToGetFieldsFor.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ reflectionCache.Add(typeToGetFieldsFor, fields);
+ }
+ return fields;
+ }
+
+ ///
+ /// Apply all the language settings to the "Greenshot" Controls on this form
+ ///
+ protected void ApplyLanguage() {
+ SuspendLayout();
+ try {
+ // Set title of the form
+ if (!string.IsNullOrEmpty(LanguageKey) && Language.TryGetString(LanguageKey, out var langString)) {
+ Text = langString;
+ }
+
+ // Reset the text values for all GreenshotControls
+ foreach (FieldInfo field in GetCachedFields(GetType())) {
+ object controlObject = field.GetValue(this);
+ if (controlObject == null) {
+ LOG.DebugFormat("No value: {0}", field.Name);
+ continue;
+ }
+
+ if (!(controlObject is Control applyToControl)) {
+ ToolStripItem applyToItem = controlObject as ToolStripItem;
+ if (applyToItem == null) {
+ LOG.DebugFormat("No Control or ToolStripItem: {0}", field.Name);
+ continue;
+ }
+ ApplyLanguage(applyToItem);
+ } else {
+ ApplyLanguage(applyToControl);
+ }
+ }
+
+ if (DesignMode) {
+ foreach (Control designControl in _designTimeControls.Values) {
+ ApplyLanguage(designControl);
+ }
+ foreach (ToolStripItem designToolStripItem in _designTimeToolStripItems.Values) {
+ ApplyLanguage(designToolStripItem);
+ }
+ }
+ } finally {
+ ResumeLayout();
+ }
+ }
+
+ ///
+ /// Apply the language text to supplied control
+ ///
+ protected void ApplyLanguage(Control applyTo, string languageKey) {
+ string langString;
+ if (!string.IsNullOrEmpty(languageKey)) {
+ if (!Language.TryGetString(languageKey, out langString)) {
+ LOG.WarnFormat("Wrong language key '{0}' configured for control '{1}'", languageKey, applyTo.Name);
+ return;
+ }
+ applyTo.Text = langString;
+ } else {
+ // Fallback to control name!
+ if (Language.TryGetString(applyTo.Name, out langString)) {
+ applyTo.Text = langString;
+ return;
+ }
+ if (!DesignMode) {
+ LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name);
+ }
+ }
+ }
+
+ ///
+ /// Fill all GreenshotControls with the values from the configuration
+ ///
+ protected void FillFields() {
+ foreach (FieldInfo field in GetCachedFields(GetType())) {
+ var controlObject = field.GetValue(this);
+ IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable;
+ if (!string.IsNullOrEmpty(configBindable?.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) {
+ IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
+ if (section != null) {
+ if (!section.Values.TryGetValue(configBindable.PropertyName, out var iniValue)) {
+ LOG.DebugFormat("Wrong property '{0}' configured for field '{1}'",configBindable.PropertyName,field.Name);
+ continue;
+ }
+
+ if (controlObject is CheckBox checkBox) {
+ checkBox.Checked = (bool)iniValue.Value;
+ checkBox.Enabled = !iniValue.IsFixed;
+ continue;
+ }
+
+ if (controlObject is RadioButton radíoButton) {
+ radíoButton.Checked = (bool)iniValue.Value;
+ radíoButton.Enabled = !iniValue.IsFixed;
+ continue;
+ }
+
+ if (controlObject is TextBox textBox) {
+ if (controlObject is HotkeyControl hotkeyControl) {
+ string hotkeyValue = (string)iniValue.Value;
+ if (!string.IsNullOrEmpty(hotkeyValue)) {
+ hotkeyControl.SetHotkey(hotkeyValue);
+ hotkeyControl.Enabled = !iniValue.IsFixed;
+ }
+ continue;
+ }
+ textBox.Text = iniValue.ToString();
+ textBox.Enabled = !iniValue.IsFixed;
+ continue;
+ }
+
+ if (controlObject is GreenshotComboBox comboxBox) {
+ comboxBox.Populate(iniValue.ValueType);
+ comboxBox.SetValue((Enum)iniValue.Value);
+ comboxBox.Enabled = !iniValue.IsFixed;
+ }
+ }
+ }
+ }
+ OnFieldsFilled();
+ }
+
+ protected virtual void OnFieldsFilled() {
+ }
+
+ ///
+ /// Store all GreenshotControl values to the configuration
+ ///
+ protected void StoreFields() {
+ bool iniDirty = false;
+ foreach (FieldInfo field in GetCachedFields(GetType())) {
+ var controlObject = field.GetValue(this);
+ IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable;
+
+ if (!string.IsNullOrEmpty(configBindable?.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) {
+ IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
+ if (section != null) {
+ if (!section.Values.TryGetValue(configBindable.PropertyName, out var iniValue)) {
+ continue;
+ }
+
+ if (controlObject is CheckBox checkBox) {
+ iniValue.Value = checkBox.Checked;
+ iniDirty = true;
+ continue;
+ }
+
+ if (controlObject is RadioButton radioButton) {
+ iniValue.Value = radioButton.Checked;
+ iniDirty = true;
+ continue;
+ }
+
+ if (controlObject is TextBox textBox) {
+ if (controlObject is HotkeyControl hotkeyControl) {
+ iniValue.Value = hotkeyControl.ToString();
+ iniDirty = true;
+ continue;
+ }
+ iniValue.UseValueOrDefault(textBox.Text);
+ iniDirty = true;
+ continue;
+ }
+
+ if (controlObject is GreenshotComboBox comboxBox) {
+ iniValue.Value = comboxBox.GetSelectedEnum();
+ iniDirty = true;
+ }
+
+ }
+ }
+ }
+ if (iniDirty) {
+ IniConfig.Save();
+ }
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotGroupBox.cs b/GreenshotPlugin/Controls/GreenshotGroupBox.cs
new file mode 100644
index 000000000..b61f64436
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotGroupBox.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Windows.Forms;
+using System.ComponentModel;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotGroupBox : GroupBox , IGreenshotLanguageBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotLabel.cs b/GreenshotPlugin/Controls/GreenshotLabel.cs
new file mode 100644
index 000000000..fa10711e0
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotLabel.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Windows.Forms;
+using System.ComponentModel;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotLabel : Label, IGreenshotLanguageBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotRadioButton.cs b/GreenshotPlugin/Controls/GreenshotRadioButton.cs
new file mode 100644
index 000000000..7ef3d41d7
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotRadioButton.cs
@@ -0,0 +1,45 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// Description of GreenshotCheckbox.
+ ///
+ public class GreenshotRadioButton : RadioButton, IGreenshotLanguageBindable, IGreenshotConfigBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+
+ [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")]
+ public string SectionName { get; set; } = "Core";
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
+ public string PropertyName {
+ get;
+ set;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Controls/GreenshotTabPage.cs b/GreenshotPlugin/Controls/GreenshotTabPage.cs
new file mode 100644
index 000000000..189c9173a
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotTabPage.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Windows.Forms;
+using System.ComponentModel;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotTabPage : TabPage, IGreenshotLanguageBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotTextBox.cs b/GreenshotPlugin/Controls/GreenshotTextBox.cs
new file mode 100644
index 000000000..e370c0b35
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotTextBox.cs
@@ -0,0 +1,36 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotTextBox : TextBox, IGreenshotConfigBindable {
+ [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")]
+ public string SectionName { get; set; } = "Core";
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
+ public string PropertyName {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotToolDropDownButton.cs b/GreenshotPlugin/Controls/GreenshotToolDropDownButton.cs
new file mode 100644
index 000000000..585334a5f
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotToolDropDownButton.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotToolStripDropDownButton : ToolStripDropDownButton, IGreenshotLanguageBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotToolStripButton.cs b/GreenshotPlugin/Controls/GreenshotToolStripButton.cs
new file mode 100644
index 000000000..c5ffff3b0
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotToolStripButton.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotToolStripButton : ToolStripButton, IGreenshotLanguageBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotToolStripLabel.cs b/GreenshotPlugin/Controls/GreenshotToolStripLabel.cs
new file mode 100644
index 000000000..2c7051c21
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotToolStripLabel.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Windows.Forms;
+using System.ComponentModel;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotToolStripLabel : ToolStripLabel, IGreenshotLanguageBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/GreenshotToolStripMenuItem.cs b/GreenshotPlugin/Controls/GreenshotToolStripMenuItem.cs
new file mode 100644
index 000000000..1adca374d
--- /dev/null
+++ b/GreenshotPlugin/Controls/GreenshotToolStripMenuItem.cs
@@ -0,0 +1,33 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Controls {
+ public class GreenshotToolStripMenuItem : ToolStripMenuItem, IGreenshotLanguageBindable {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/HotkeyControl.cs b/GreenshotPlugin/Controls/HotkeyControl.cs
new file mode 100644
index 000000000..89f0889b2
--- /dev/null
+++ b/GreenshotPlugin/Controls/HotkeyControl.cs
@@ -0,0 +1,582 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Windows.Forms;
+using log4net;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// A simple control that allows the user to select pretty much any valid hotkey combination
+ /// See: http://www.codeproject.com/KB/buttons/hotkeycontrol.aspx
+ /// But is modified to fit in Greenshot, and have localized support
+ ///
+ public sealed class HotkeyControl : GreenshotTextBox {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(HotkeyControl));
+
+ private static readonly EventDelay EventDelay = new EventDelay(TimeSpan.FromMilliseconds(600).Ticks);
+ private static readonly bool IsWindows7OrOlder = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 1;
+
+ // Holds the list of hotkeys
+ private static readonly IDictionary KeyHandlers = new Dictionary();
+ private static int _hotKeyCounter = 1;
+ private const uint WM_HOTKEY = 0x312;
+ private static IntPtr _hotkeyHwnd;
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum Modifiers : uint {
+ NONE = 0,
+ ALT = 1,
+ CTRL = 2,
+ SHIFT = 4,
+ WIN = 8,
+ NO_REPEAT = 0x4000
+ }
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ private enum MapType : uint {
+ MAPVK_VK_TO_VSC = 0, //The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the function returns 0.
+ MAPVK_VSC_TO_VK = 1, //The uCode parameter is a scan code and is translated into a virtual-key code that does not distinguish between left- and right-hand keys. If there is no translation, the function returns 0.
+ MAPVK_VK_TO_CHAR = 2, //The uCode parameter is a virtual-key code and is translated into an unshifted character value in the low order word of the return value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation, the function returns 0.
+ MAPVK_VSC_TO_VK_EX = 3, //The uCode parameter is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand keys. If there is no translation, the function returns 0.
+ MAPVK_VK_TO_VSC_EX = 4 //The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If the scan code is an extended scan code, the high byte of the uCode value can contain either 0xe0 or 0xe1 to specify the extended scan code. If there is no translation, the function returns 0.
+ }
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint virtualKeyCode);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern uint MapVirtualKey(uint uCode, uint uMapType);
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ private static extern int GetKeyNameText(uint lParam, [Out] StringBuilder lpString, int nSize);
+
+ // These variables store the current hotkey and modifier(s)
+ private Keys _hotkey = Keys.None;
+ private Keys _modifiers = Keys.None;
+
+ // ArrayLists used to enforce the use of proper modifiers.
+ // Shift+A isn't a valid hotkey, for instance, as it would screw up when the user is typing.
+ private readonly IList _needNonShiftModifier = new List();
+ private readonly IList _needNonAltGrModifier = new List();
+
+ private readonly ContextMenuStrip _dummy = new ContextMenuStrip();
+
+ ///
+ /// Used to make sure that there is no right-click menu available
+ ///
+ public override ContextMenuStrip ContextMenuStrip
+ {
+ get {
+ return _dummy;
+ }
+ set {
+ base.ContextMenuStrip = _dummy;
+ }
+ }
+
+ ///
+ /// Forces the control to be non-multiline
+ ///
+ public override bool Multiline {
+ get {
+ return base.Multiline;
+ }
+ set {
+ // Ignore what the user wants; force Multiline to false
+ base.Multiline = false;
+ }
+ }
+
+ ///
+ /// Creates a new HotkeyControl
+ ///
+ public HotkeyControl() {
+ ContextMenuStrip = _dummy; // Disable right-clicking
+ Text = "None";
+
+ // Handle events that occurs when keys are pressed
+ KeyPress += HotkeyControl_KeyPress;
+ KeyUp += HotkeyControl_KeyUp;
+ KeyDown += HotkeyControl_KeyDown;
+
+ PopulateModifierLists();
+ }
+
+ ///
+ /// Populates the ArrayLists specifying disallowed hotkeys
+ /// such as Shift+A, Ctrl+Alt+4 (would produce a dollar sign) etc
+ ///
+ private void PopulateModifierLists() {
+ // Shift + 0 - 9, A - Z
+ for (Keys k = Keys.D0; k <= Keys.Z; k++) {
+ _needNonShiftModifier.Add((int)k);
+ }
+
+ // Shift + Numpad keys
+ for (Keys k = Keys.NumPad0; k <= Keys.NumPad9; k++) {
+ _needNonShiftModifier.Add((int)k);
+ }
+
+ // Shift + Misc (,;<./ etc)
+ for (Keys k = Keys.Oem1; k <= Keys.OemBackslash; k++) {
+ _needNonShiftModifier.Add((int)k);
+ }
+
+ // Shift + Space, PgUp, PgDn, End, Home
+ for (Keys k = Keys.Space; k <= Keys.Home; k++) {
+ _needNonShiftModifier.Add((int)k);
+ }
+
+ // Misc keys that we can't loop through
+ _needNonShiftModifier.Add((int)Keys.Insert);
+ _needNonShiftModifier.Add((int)Keys.Help);
+ _needNonShiftModifier.Add((int)Keys.Multiply);
+ _needNonShiftModifier.Add((int)Keys.Add);
+ _needNonShiftModifier.Add((int)Keys.Subtract);
+ _needNonShiftModifier.Add((int)Keys.Divide);
+ _needNonShiftModifier.Add((int)Keys.Decimal);
+ _needNonShiftModifier.Add((int)Keys.Return);
+ _needNonShiftModifier.Add((int)Keys.Escape);
+ _needNonShiftModifier.Add((int)Keys.NumLock);
+
+ // Ctrl+Alt + 0 - 9
+ for (Keys k = Keys.D0; k <= Keys.D9; k++) {
+ _needNonAltGrModifier.Add((int)k);
+ }
+ }
+
+ ///
+ /// Resets this hotkey control to None
+ ///
+ public new void Clear() {
+ Hotkey = Keys.None;
+ HotkeyModifiers = Keys.None;
+ }
+
+ ///
+ /// Fires when a key is pushed down. Here, we'll want to update the text in the box
+ /// to notify the user what combination is currently pressed.
+ ///
+ private void HotkeyControl_KeyDown(object sender, KeyEventArgs e) {
+ // Clear the current hotkey
+ if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete) {
+ ResetHotkey();
+ } else {
+ _modifiers = e.Modifiers;
+ _hotkey = e.KeyCode;
+ Redraw();
+ }
+ }
+
+ ///
+ /// Fires when all keys are released. If the current hotkey isn't valid, reset it.
+ /// Otherwise, do nothing and keep the text and hotkey as it was.
+ ///
+ private void HotkeyControl_KeyUp(object sender, KeyEventArgs e) {
+ // Somehow the PrintScreen only comes as a keyup, therefore we handle it here.
+ if (e.KeyCode == Keys.PrintScreen) {
+ _modifiers = e.Modifiers;
+ _hotkey = e.KeyCode;
+ Redraw();
+ }
+
+ if (_hotkey == Keys.None && ModifierKeys == Keys.None) {
+ ResetHotkey();
+ }
+ }
+
+ ///
+ /// Prevents the letter/whatever entered to show up in the TextBox
+ /// Without this, a "A" key press would appear as "aControl, Alt + A"
+ ///
+ private void HotkeyControl_KeyPress(object sender, KeyPressEventArgs e) {
+ e.Handled = true;
+ }
+
+ ///
+ /// Handles some misc keys, such as Ctrl+Delete and Shift+Insert
+ ///
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
+ if (keyData == Keys.Delete || keyData == (Keys.Control | Keys.Delete)) {
+ ResetHotkey();
+ return true;
+ }
+
+ // Paste
+ if (keyData == (Keys.Shift | Keys.Insert)) {
+ return true; // Don't allow
+ }
+
+ // Allow the rest
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ ///
+ /// Clears the current hotkey and resets the TextBox
+ ///
+ public void ResetHotkey() {
+ _hotkey = Keys.None;
+ _modifiers = Keys.None;
+ Redraw();
+ }
+
+ ///
+ /// Used to get/set the hotkey (e.g. Keys.A)
+ ///
+ public Keys Hotkey {
+ get {
+ return _hotkey;
+ }
+ set {
+ _hotkey = value;
+ Redraw(true);
+ }
+ }
+
+ ///
+ /// Used to get/set the hotkey (e.g. Keys.A)
+ ///
+ public void SetHotkey(string hotkey) {
+ _hotkey = HotkeyFromString(hotkey);
+ _modifiers = HotkeyModifiersFromString(hotkey);
+ Redraw(true);
+ }
+
+ ///
+ /// Used to get/set the modifier keys (e.g. Keys.Alt | Keys.Control)
+ ///
+ public Keys HotkeyModifiers {
+ get {
+ return _modifiers;
+ }
+ set {
+ _modifiers = value;
+ Redraw(true);
+ }
+ }
+
+ ///
+ /// Redraws the TextBox when necessary.
+ ///
+ /// Specifies whether this function was called by the Hotkey/HotkeyModifiers properties or by the user.
+ private void Redraw(bool bCalledProgramatically = false) {
+ // No hotkey set
+ if (_hotkey == Keys.None) {
+ Text = string.Empty;
+ return;
+ }
+
+ // LWin/RWin doesn't work as hotkeys (neither do they work as modifier keys in .NET 2.0)
+ if (_hotkey == Keys.LWin || _hotkey == Keys.RWin) {
+ Text = string.Empty;
+ return;
+ }
+
+ // Only validate input if it comes from the user
+ if (bCalledProgramatically == false) {
+ // No modifier or shift only, AND a hotkey that needs another modifier
+ if ((_modifiers == Keys.Shift || _modifiers == Keys.None) && _needNonShiftModifier.Contains((int)_hotkey)) {
+ if (_modifiers == Keys.None) {
+ // Set Ctrl+Alt as the modifier unless Ctrl+Alt+ won't work...
+ if (_needNonAltGrModifier.Contains((int)_hotkey) == false) {
+ _modifiers = Keys.Alt | Keys.Control;
+ } else {
+ // ... in that case, use Shift+Alt instead.
+ _modifiers = Keys.Alt | Keys.Shift;
+ }
+ } else {
+ // User pressed Shift and an invalid key (e.g. a letter or a number),
+ // that needs another set of modifier keys
+ _hotkey = Keys.None;
+ Text = string.Empty;
+ return;
+ }
+ }
+ // Check all Ctrl+Alt keys
+ if ((_modifiers == (Keys.Alt | Keys.Control)) && _needNonAltGrModifier.Contains((int)_hotkey)) {
+ // Ctrl+Alt+4 etc won't work; reset hotkey and tell the user
+ _hotkey = Keys.None;
+ Text = string.Empty;
+ return;
+ }
+ }
+
+ // I have no idea why this is needed, but it is. Without this code, pressing only Ctrl
+ // will show up as "Control + ControlKey", etc.
+ if (_hotkey == Keys.Menu /* Alt */ || _hotkey == Keys.ShiftKey || _hotkey == Keys.ControlKey) {
+ _hotkey = Keys.None;
+ }
+ Text = HotkeyToLocalizedString(_modifiers, _hotkey);
+ }
+
+ public override string ToString() {
+ return HotkeyToString(HotkeyModifiers, Hotkey);
+ }
+
+ public static string GetLocalizedHotkeyStringFromString(string hotkeyString) {
+ Keys virtualKeyCode = HotkeyFromString(hotkeyString);
+ Keys modifiers = HotkeyModifiersFromString(hotkeyString);
+ return HotkeyToLocalizedString(modifiers, virtualKeyCode);
+ }
+
+ public static string HotkeyToString(Keys modifierKeyCode, Keys virtualKeyCode) {
+ return HotkeyModifiersToString(modifierKeyCode) + virtualKeyCode;
+ }
+
+ public static string HotkeyModifiersToString(Keys modifierKeyCode) {
+ StringBuilder hotkeyString = new StringBuilder();
+ if ((modifierKeyCode & Keys.Alt) > 0) {
+ hotkeyString.Append("Alt").Append(" + ");
+ }
+ if ((modifierKeyCode & Keys.Control) > 0) {
+ hotkeyString.Append("Ctrl").Append(" + ");
+ }
+ if ((modifierKeyCode & Keys.Shift) > 0) {
+ hotkeyString.Append("Shift").Append(" + ");
+ }
+ if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin) {
+ hotkeyString.Append("Win").Append(" + ");
+ }
+ return hotkeyString.ToString();
+ }
+
+
+ public static string HotkeyToLocalizedString(Keys modifierKeyCode, Keys virtualKeyCode) {
+ return HotkeyModifiersToLocalizedString(modifierKeyCode) + GetKeyName(virtualKeyCode);
+ }
+
+ public static string HotkeyModifiersToLocalizedString(Keys modifierKeyCode) {
+ StringBuilder hotkeyString = new StringBuilder();
+ if ((modifierKeyCode & Keys.Alt) > 0) {
+ hotkeyString.Append(GetKeyName(Keys.Alt)).Append(" + ");
+ }
+ if ((modifierKeyCode & Keys.Control) > 0) {
+ hotkeyString.Append(GetKeyName(Keys.Control)).Append(" + ");
+ }
+ if ((modifierKeyCode & Keys.Shift) > 0) {
+ hotkeyString.Append(GetKeyName(Keys.Shift)).Append(" + ");
+ }
+ if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin) {
+ hotkeyString.Append("Win").Append(" + ");
+ }
+ return hotkeyString.ToString();
+ }
+
+
+ public static Keys HotkeyModifiersFromString(string modifiersString) {
+ Keys modifiers = Keys.None;
+ if (!string.IsNullOrEmpty(modifiersString)) {
+ if (modifiersString.ToLower().Contains("alt")) {
+ modifiers |= Keys.Alt;
+ }
+ if (modifiersString.ToLower().Contains("ctrl")) {
+ modifiers |= Keys.Control;
+ }
+ if (modifiersString.ToLower().Contains("shift")) {
+ modifiers |= Keys.Shift;
+ }
+ if (modifiersString.ToLower().Contains("win")) {
+ modifiers |= Keys.LWin;
+ }
+ }
+ return modifiers;
+ }
+
+ public static Keys HotkeyFromString(string hotkey) {
+ Keys key = Keys.None;
+ if (!string.IsNullOrEmpty(hotkey)) {
+ if (hotkey.LastIndexOf('+') > 0) {
+ hotkey = hotkey.Remove(0,hotkey.LastIndexOf('+')+1).Trim();
+ }
+ key = (Keys)Enum.Parse(typeof(Keys), hotkey);
+ }
+ return key;
+ }
+
+ public static void RegisterHotkeyHwnd(IntPtr hWnd) {
+ _hotkeyHwnd = hWnd;
+ }
+
+ public static int RegisterHotKey(string hotkey, HotKeyHandler handler) {
+ return RegisterHotKey(HotkeyModifiersFromString(hotkey), HotkeyFromString(hotkey),handler);
+ }
+
+ ///
+ /// Register a hotkey
+ ///
+ /// The modifier, e.g.: Modifiers.CTRL, Modifiers.NONE or Modifiers.ALT
+ /// The virtual key code
+ /// A HotKeyHandler, this will be called to handle the hotkey press
+ /// the hotkey number, -1 if failed
+ public static int RegisterHotKey(Keys modifierKeyCode, Keys virtualKeyCode, HotKeyHandler handler) {
+ if (virtualKeyCode == Keys.None) {
+ Log.Warn("Trying to register a Keys.none hotkey, ignoring");
+ return 0;
+ }
+ // Convert Modifiers to fit HKM_SETHOTKEY
+ uint modifiers = 0;
+ if ((modifierKeyCode & Keys.Alt) > 0) {
+ modifiers |= (uint)Modifiers.ALT;
+ }
+ if ((modifierKeyCode & Keys.Control) > 0) {
+ modifiers |= (uint)Modifiers.CTRL;
+ }
+ if ((modifierKeyCode & Keys.Shift) > 0) {
+ modifiers |= (uint)Modifiers.SHIFT;
+ }
+ if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin) {
+ modifiers |= (uint)Modifiers.WIN;
+ }
+ // Disable repeating hotkey for Windows 7 and beyond, as described in #1559
+ if (IsWindows7OrOlder) {
+ modifiers |= (uint)Modifiers.NO_REPEAT;
+ }
+ if (RegisterHotKey(_hotkeyHwnd, _hotKeyCounter, modifiers, (uint)virtualKeyCode)) {
+ KeyHandlers.Add(_hotKeyCounter, handler);
+ return _hotKeyCounter++;
+ } else {
+ Log.Warn($"Couldn't register hotkey modifier {modifierKeyCode} virtualKeyCode {virtualKeyCode}");
+ return -1;
+ }
+ }
+
+ public static void UnregisterHotkeys() {
+ foreach(int hotkey in KeyHandlers.Keys) {
+ UnregisterHotKey(_hotkeyHwnd, hotkey);
+ }
+ // Remove all key handlers
+ KeyHandlers.Clear();
+ }
+
+ public static void UnregisterHotkey(int hotkey) {
+ bool removeHotkey = false;
+ foreach(int availableHotkey in KeyHandlers.Keys) {
+ if (availableHotkey == hotkey) {
+ UnregisterHotKey(_hotkeyHwnd, hotkey);
+ removeHotkey = true;
+ }
+ }
+ if (removeHotkey) {
+ // Remove key handler
+ KeyHandlers.Remove(hotkey);
+ }
+ }
+
+ ///
+ /// Handle WndProc messages for the hotkey
+ ///
+ ///
+ /// true if the message was handled
+ public static bool HandleMessages(ref Message m) {
+ if (m.Msg != WM_HOTKEY)
+ {
+ return false;
+ }
+ // Call handler
+ if (!IsWindows7OrOlder && !EventDelay.Check())
+ {
+ return true;
+ }
+
+ if (KeyHandlers.TryGetValue((int)m.WParam, out var handler))
+ {
+ handler();
+ }
+ return true;
+ }
+
+ public static string GetKeyName(Keys givenKey) {
+ StringBuilder keyName = new StringBuilder();
+ const uint numpad = 55;
+
+ Keys virtualKey = givenKey;
+ string keyString;
+ // Make VC's to real keys
+ switch(virtualKey) {
+ case Keys.Alt:
+ virtualKey = Keys.LMenu;
+ break;
+ case Keys.Control:
+ virtualKey = Keys.ControlKey;
+ break;
+ case Keys.Shift:
+ virtualKey = Keys.LShiftKey;
+ break;
+ case Keys.Multiply:
+ GetKeyNameText(numpad << 16, keyName, 100);
+ keyString = keyName.ToString().Replace("*", string.Empty).Trim().ToLower();
+ if (keyString.IndexOf("(", StringComparison.Ordinal) >= 0) {
+ return "* " + keyString;
+ }
+ keyString = keyString.Substring(0,1).ToUpper() + keyString.Substring(1).ToLower();
+ return keyString + " *";
+ case Keys.Divide:
+ GetKeyNameText(numpad << 16, keyName, 100);
+ keyString = keyName.ToString().Replace("*", string.Empty).Trim().ToLower();
+ if (keyString.IndexOf("(", StringComparison.Ordinal) >= 0) {
+ return "/ " + keyString;
+ }
+ keyString = keyString.Substring(0,1).ToUpper() + keyString.Substring(1).ToLower();
+ return keyString + " /";
+ }
+ uint scanCode = MapVirtualKey((uint)virtualKey, (uint)MapType.MAPVK_VK_TO_VSC);
+
+ // because MapVirtualKey strips the extended bit for some keys
+ switch (virtualKey) {
+ case Keys.Left: case Keys.Up: case Keys.Right: case Keys.Down: // arrow keys
+ case Keys.Prior: case Keys.Next: // page up and page down
+ case Keys.End: case Keys.Home:
+ case Keys.Insert: case Keys.Delete:
+ case Keys.NumLock:
+ Log.Debug("Modifying Extended bit");
+ scanCode |= 0x100; // set extended bit
+ break;
+ case Keys.PrintScreen: // PrintScreen
+ scanCode = 311;
+ break;
+ case Keys.Pause: // PrintScreen
+ scanCode = 69;
+ break;
+ }
+ scanCode |= 0x200;
+ if (GetKeyNameText(scanCode << 16, keyName, 100) != 0) {
+ string visibleName = keyName.ToString();
+ if (visibleName.Length > 1) {
+ visibleName = visibleName.Substring(0,1) + visibleName.Substring(1).ToLower();
+ }
+ return visibleName;
+ } else {
+ return givenKey.ToString();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Controls/IGreenshotConfigBindable.cs b/GreenshotPlugin/Controls/IGreenshotConfigBindable.cs
new file mode 100644
index 000000000..7211ed402
--- /dev/null
+++ b/GreenshotPlugin/Controls/IGreenshotConfigBindable.cs
@@ -0,0 +1,40 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotPlugin.Controls {
+ public interface IGreenshotConfigBindable {
+ ///
+ /// The class where the property-value is stored
+ ///
+ string SectionName {
+ get;
+ set;
+ }
+
+ ///
+ /// Path to the property value which will be mapped with this control
+ ///
+ string PropertyName {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/IGreenshotLanguageBindable.cs b/GreenshotPlugin/Controls/IGreenshotLanguageBindable.cs
new file mode 100644
index 000000000..7b5775a22
--- /dev/null
+++ b/GreenshotPlugin/Controls/IGreenshotLanguageBindable.cs
@@ -0,0 +1,36 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// This interface describes the designer fields that need to be implemented for Greenshot controls
+ ///
+ public interface IGreenshotLanguageBindable {
+ ///
+ /// Language key to use to fill the Text value with
+ ///
+ string LanguageKey {
+ get;
+ set;
+ }
+
+ }
+}
diff --git a/src/Greenshot.Base/Controls/OAuthLoginForm.Designer.cs b/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs
similarity index 86%
rename from src/Greenshot.Base/Controls/OAuthLoginForm.Designer.cs
rename to GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs
index cac4d8528..572829a76 100644
--- a/src/Greenshot.Base/Controls/OAuthLoginForm.Designer.cs
+++ b/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs
@@ -1,92 +1,92 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: https://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-namespace Greenshot.Base.Controls {
- partial class OAuthLoginForm {
- ///
- /// Required designer variable.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null)) {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Windows Form Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent() {
- this._addressTextBox = new System.Windows.Forms.TextBox();
- this._browser = new ExtendedWebBrowser();
- this.SuspendLayout();
- //
- // _addressTextBox
- //
- this._addressTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
- this._addressTextBox.Dock = System.Windows.Forms.DockStyle.Top;
- this._addressTextBox.Enabled = false;
- this._addressTextBox.Location = new System.Drawing.Point(0, 0);
- this._addressTextBox.Name = "addressTextBox";
- this._addressTextBox.Size = new System.Drawing.Size(595, 20);
- this._addressTextBox.TabIndex = 3;
- this._addressTextBox.TabStop = false;
- //
- // _browser
- //
- this._browser.Dock = System.Windows.Forms.DockStyle.Fill;
- this._browser.Location = new System.Drawing.Point(0, 20);
- this._browser.MinimumSize = new System.Drawing.Size(100, 100);
- this._browser.Name = "browser";
- this._browser.Size = new System.Drawing.Size(595, 295);
- this._browser.TabIndex = 4;
- //
- // OAuthLoginForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(595, 315);
- this.Controls.Add(this._browser);
- this.Controls.Add(this._addressTextBox);
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "OAuthLoginForm";
- this.ResumeLayout(false);
- this.PerformLayout();
-
- }
-
- #endregion
-
- private System.Windows.Forms.TextBox _addressTextBox;
- private ExtendedWebBrowser _browser;
-
- }
-}
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace GreenshotPlugin.Controls {
+ partial class OAuthLoginForm {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ this._addressTextBox = new System.Windows.Forms.TextBox();
+ this._browser = new ExtendedWebBrowser();
+ this.SuspendLayout();
+ //
+ // _addressTextBox
+ //
+ this._addressTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
+ this._addressTextBox.Dock = System.Windows.Forms.DockStyle.Top;
+ this._addressTextBox.Enabled = false;
+ this._addressTextBox.Location = new System.Drawing.Point(0, 0);
+ this._addressTextBox.Name = "addressTextBox";
+ this._addressTextBox.Size = new System.Drawing.Size(595, 20);
+ this._addressTextBox.TabIndex = 3;
+ this._addressTextBox.TabStop = false;
+ //
+ // _browser
+ //
+ this._browser.Dock = System.Windows.Forms.DockStyle.Fill;
+ this._browser.Location = new System.Drawing.Point(0, 20);
+ this._browser.MinimumSize = new System.Drawing.Size(100, 100);
+ this._browser.Name = "browser";
+ this._browser.Size = new System.Drawing.Size(595, 295);
+ this._browser.TabIndex = 4;
+ //
+ // OAuthLoginForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(595, 315);
+ this.Controls.Add(this._browser);
+ this.Controls.Add(this._addressTextBox);
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "OAuthLoginForm";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox _addressTextBox;
+ private ExtendedWebBrowser _browser;
+
+ }
+}
diff --git a/GreenshotPlugin/Controls/OAuthLoginForm.cs b/GreenshotPlugin/Controls/OAuthLoginForm.cs
new file mode 100644
index 000000000..2b834d128
--- /dev/null
+++ b/GreenshotPlugin/Controls/OAuthLoginForm.cs
@@ -0,0 +1,108 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using GreenshotPlugin.Core;
+using log4net;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// The OAuthLoginForm is used to allow the user to authorize Greenshot with an "Oauth" application
+ ///
+ public sealed partial class OAuthLoginForm : Form {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(OAuthLoginForm));
+ private readonly string _callbackUrl;
+ private IDictionary _callbackParameters;
+
+ public IDictionary CallbackParameters => _callbackParameters;
+
+ public bool IsOk => DialogResult == DialogResult.OK;
+
+ public OAuthLoginForm(string browserTitle, Size size, string authorizationLink, string callbackUrl) {
+ // Make sure Greenshot uses the correct browser version
+ IEHelper.FixBrowserVersion(false);
+
+ _callbackUrl = callbackUrl;
+ // Fix for BUG-2071
+ if (callbackUrl.EndsWith("/"))
+ {
+ _callbackUrl = callbackUrl.Substring(0, callbackUrl.Length - 1);
+ }
+ InitializeComponent();
+ ClientSize = size;
+ Icon = GreenshotResources.GetGreenshotIcon();
+ Text = browserTitle;
+ _addressTextBox.Text = authorizationLink;
+
+ // The script errors are suppressed by using the ExtendedWebBrowser
+ _browser.ScriptErrorsSuppressed = false;
+ _browser.DocumentCompleted += Browser_DocumentCompleted;
+ _browser.Navigated += Browser_Navigated;
+ _browser.Navigating += Browser_Navigating;
+ _browser.Navigate(new Uri(authorizationLink));
+ }
+
+ ///
+ /// Make sure the form is visible
+ ///
+ /// EventArgs
+ protected override void OnShown(EventArgs e) {
+ base.OnShown(e);
+ WindowDetails.ToForeground(Handle);
+ }
+
+ private void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
+ LOG.DebugFormat("document completed with url: {0}", _browser.Url);
+ CheckUrl();
+ }
+
+ private void Browser_Navigating(object sender, WebBrowserNavigatingEventArgs e) {
+ LOG.DebugFormat("Navigating to url: {0}", _browser.Url);
+ _addressTextBox.Text = e.Url.ToString();
+ }
+
+ private void Browser_Navigated(object sender, WebBrowserNavigatedEventArgs e) {
+ LOG.DebugFormat("Navigated to url: {0}", _browser.Url);
+ CheckUrl();
+ }
+
+ private void CheckUrl() {
+ if (_browser.Url.ToString().StartsWith(_callbackUrl)) {
+ var correctedUri = new Uri(_browser.Url.AbsoluteUri.Replace("#", "&"));
+
+ string queryParams = correctedUri.Query;
+ if (queryParams.Length > 0) {
+ queryParams = NetworkHelper.UrlDecode(queryParams);
+ //Store the Token and Token Secret
+ _callbackParameters = NetworkHelper.ParseQueryString(queryParams);
+ }
+ DialogResult = DialogResult.OK;
+ }
+ }
+
+ private void AddressTextBox_KeyPress(object sender, KeyPressEventArgs e) {
+ //Cancel the key press so the user can't enter a new url
+ e.Handled = true;
+ }
+ }
+}
diff --git a/src/Greenshot.Base/Controls/PleaseWaitForm.Designer.cs b/GreenshotPlugin/Controls/PleaseWaitForm.Designer.cs
similarity index 87%
rename from src/Greenshot.Base/Controls/PleaseWaitForm.Designer.cs
rename to GreenshotPlugin/Controls/PleaseWaitForm.Designer.cs
index 26fc19d38..d287b3dde 100644
--- a/src/Greenshot.Base/Controls/PleaseWaitForm.Designer.cs
+++ b/GreenshotPlugin/Controls/PleaseWaitForm.Designer.cs
@@ -1,99 +1,101 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: https://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-namespace Greenshot.Base.Controls {
- partial class PleaseWaitForm {
- ///
- /// Designer variable used to keep track of non-visual components.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Disposes resources used by the form.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing) {
- if (disposing) {
- if (components != null) {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
-
- ///
- /// This method is required for Windows Forms designer support.
- /// Do not change the method contents inside the source code editor. The Forms designer might
- /// not be able to load this method if it was changed manually.
- ///
- private void InitializeComponent() {
- this.label_pleasewait = new System.Windows.Forms.Label();
- this.cancelButton = new System.Windows.Forms.Button();
- this.SuspendLayout();
- //
- // label_pleasewait
- //
- this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill;
- this.label_pleasewait.Location = new System.Drawing.Point(0, 0);
- this.label_pleasewait.Name = "label_pleasewait";
- this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10);
- this.label_pleasewait.Size = new System.Drawing.Size(90, 33);
- this.label_pleasewait.TabIndex = 0;
- this.label_pleasewait.Text = "Please wait...";
- this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
- this.label_pleasewait.UseWaitCursor = true;
- //
- // cancelButton
- //
- this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.cancelButton.Location = new System.Drawing.Point(38, 41);
- this.cancelButton.Name = "cancelButton";
- this.cancelButton.Size = new System.Drawing.Size(94, 23);
- this.cancelButton.TabIndex = 1;
- this.cancelButton.Text = "Cancel";
- this.cancelButton.UseVisualStyleBackColor = true;
- this.cancelButton.UseWaitCursor = true;
- this.cancelButton.Click += new System.EventHandler(this.CancelButtonClick);
- //
- // PleaseWaitForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.CancelButton = this.cancelButton;
- this.ClientSize = new System.Drawing.Size(169, 76);
- this.Controls.Add(this.cancelButton);
- this.Controls.Add(this.label_pleasewait);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "PleaseWaitForm";
- this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Greenshot";
- this.UseWaitCursor = true;
- this.ResumeLayout(false);
- this.PerformLayout();
- }
- private System.Windows.Forms.Button cancelButton;
- private System.Windows.Forms.Label label_pleasewait;
- }
-}
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace GreenshotPlugin.Controls {
+ partial class PleaseWaitForm {
+ ///
+ /// Designer variable used to keep track of non-visual components.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Disposes resources used by the form.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing) {
+ if (disposing) {
+ if (components != null) {
+ components.Dispose();
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// This method is required for Windows Forms designer support.
+ /// Do not change the method contents inside the source code editor. The Forms designer might
+ /// not be able to load this method if it was changed manually.
+ ///
+ private void InitializeComponent() {
+ this.label_pleasewait = new System.Windows.Forms.Label();
+ this.cancelButton = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // label_pleasewait
+ //
+ this.label_pleasewait.AutoSize = true;
+ this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.label_pleasewait.Location = new System.Drawing.Point(0, 0);
+ this.label_pleasewait.Name = "label_pleasewait";
+ this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10);
+ this.label_pleasewait.Size = new System.Drawing.Size(90, 33);
+ this.label_pleasewait.TabIndex = 0;
+ this.label_pleasewait.Text = "Please wait...";
+ this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.label_pleasewait.UseWaitCursor = true;
+ //
+ // cancelButton
+ //
+ this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.cancelButton.Location = new System.Drawing.Point(38, 41);
+ this.cancelButton.Name = "cancelButton";
+ this.cancelButton.Size = new System.Drawing.Size(94, 23);
+ this.cancelButton.TabIndex = 1;
+ this.cancelButton.Text = "Cancel";
+ this.cancelButton.UseVisualStyleBackColor = true;
+ this.cancelButton.UseWaitCursor = true;
+ this.cancelButton.Click += new System.EventHandler(this.CancelButtonClick);
+ //
+ // PleaseWaitForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoSize = true;
+ this.CancelButton = this.cancelButton;
+ this.ClientSize = new System.Drawing.Size(169, 76);
+ this.Controls.Add(this.cancelButton);
+ this.Controls.Add(this.label_pleasewait);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "PleaseWaitForm";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Greenshot";
+ this.UseWaitCursor = true;
+ this.ResumeLayout(false);
+ this.PerformLayout();
+ }
+ private System.Windows.Forms.Button cancelButton;
+ private System.Windows.Forms.Label label_pleasewait;
+ }
+}
diff --git a/GreenshotPlugin/Controls/PleaseWaitForm.cs b/GreenshotPlugin/Controls/PleaseWaitForm.cs
new file mode 100644
index 000000000..995b0c17c
--- /dev/null
+++ b/GreenshotPlugin/Controls/PleaseWaitForm.cs
@@ -0,0 +1,124 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Windows.Forms;
+using System.Threading;
+using GreenshotPlugin.Core;
+using log4net;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// Description of PleaseWaitForm.
+ ///
+ public partial class PleaseWaitForm : Form {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(PleaseWaitForm));
+ private Thread _waitFor;
+ private string _title;
+ public PleaseWaitForm() {
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+ Icon = GreenshotResources.GetGreenshotIcon();
+ }
+
+ ///
+ /// Prevent the close-window button showing
+ ///
+ private const int CP_NOCLOSE_BUTTON = 0x200;
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams createParams = base.CreateParams;
+ createParams.ClassStyle |= CP_NOCLOSE_BUTTON ;
+ return createParams;
+ }
+ }
+
+ ///
+ /// Show the "please wait" form, execute the code from the delegate and wait until execution finishes.
+ /// The supplied delegate will be wrapped with a try/catch so this method can return any exception that was thrown.
+ ///
+ /// The title of the form (and Thread)
+ /// The text in the form
+ /// delegate { with your code }
+ public void ShowAndWait(string title, string text, ThreadStart waitDelegate) {
+ _title = title;
+ Text = title;
+ label_pleasewait.Text = text;
+ cancelButton.Text = Language.GetString("CANCEL");
+
+ // Make sure the form is shown.
+ Show();
+
+ // Variable to store the exception, if one is generated, from inside the thread.
+ Exception threadException = null;
+ try {
+ // Wrap the passed delegate in a try/catch which makes it possible to save the exception
+ _waitFor = new Thread(new ThreadStart(
+ delegate
+ {
+ try
+ {
+ waitDelegate.Invoke();
+ }
+ catch (Exception ex)
+ {
+ LOG.Error("invoke error:", ex);
+ threadException = ex;
+ }
+ })
+ )
+ {
+ Name = title,
+ IsBackground = true
+ };
+ _waitFor.SetApartmentState(ApartmentState.STA);
+ _waitFor.Start();
+
+ // Wait until finished
+ while (!_waitFor.Join(TimeSpan.FromMilliseconds(100))) {
+ Application.DoEvents();
+ }
+ LOG.DebugFormat("Finished {0}", title);
+ } catch (Exception ex) {
+ LOG.Error(ex);
+ throw;
+ } finally {
+ Close();
+ }
+ // Check if an exception occured, if so throw it
+ if (threadException != null) {
+ throw threadException;
+ }
+ }
+
+ ///
+ /// Called if the cancel button is clicked, will use Thread.Abort()
+ ///
+ ///
+ ///
+ private void CancelButtonClick(object sender, EventArgs e) {
+ LOG.DebugFormat("Cancel clicked on {0}", _title);
+ cancelButton.Enabled = false;
+ _waitFor.Abort();
+ }
+ }
+}
diff --git a/src/Greenshot.Base/Controls/QualityDialog.Designer.cs b/GreenshotPlugin/Controls/QualityDialog.Designer.cs
similarity index 86%
rename from src/Greenshot.Base/Controls/QualityDialog.Designer.cs
rename to GreenshotPlugin/Controls/QualityDialog.Designer.cs
index 725b17328..cf10c3a2e 100644
--- a/src/Greenshot.Base/Controls/QualityDialog.Designer.cs
+++ b/GreenshotPlugin/Controls/QualityDialog.Designer.cs
@@ -1,147 +1,148 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: https://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-namespace Greenshot.Base.Controls {
- partial class QualityDialog {
- ///
- /// Designer variable used to keep track of non-visual components.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Disposes resources used by the form.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing) {
- if (components != null) {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
-
- ///
- /// This method is required for Windows Forms designer support.
- /// Do not change the method contents inside the source code editor. The Forms designer might
- /// not be able to load this method if it was changed manually.
- ///
- private void InitializeComponent()
- {
- this.label_choosejpegquality = new GreenshotLabel();
- this.textBoxJpegQuality = new System.Windows.Forms.TextBox();
- this.trackBarJpegQuality = new System.Windows.Forms.TrackBar();
- this.checkbox_dontaskagain = new GreenshotCheckBox();
- this.button_ok = new GreenshotButton();
- this.checkBox_reduceColors = new System.Windows.Forms.CheckBox();
- ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).BeginInit();
- this.SuspendLayout();
- //
- // label_choosejpegquality
- //
- this.label_choosejpegquality.Location = new System.Drawing.Point(12, 47);
- this.label_choosejpegquality.Name = "label_choosejpegquality";
- this.label_choosejpegquality.Size = new System.Drawing.Size(268, 19);
- this.label_choosejpegquality.TabIndex = 15;
- this.label_choosejpegquality.LanguageKey = "jpegqualitydialog_choosejpegquality";
- //
- // textBoxJpegQuality
- //
- this.textBoxJpegQuality.Location = new System.Drawing.Point(245, 69);
- this.textBoxJpegQuality.Name = "textBoxJpegQuality";
- this.textBoxJpegQuality.ReadOnly = true;
- this.textBoxJpegQuality.Size = new System.Drawing.Size(35, 20);
- this.textBoxJpegQuality.TabIndex = 4;
- this.textBoxJpegQuality.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
- //
- // trackBarJpegQuality
- //
- this.trackBarJpegQuality.LargeChange = 10;
- this.trackBarJpegQuality.Location = new System.Drawing.Point(12, 69);
- this.trackBarJpegQuality.Maximum = 100;
- this.trackBarJpegQuality.Name = "trackBarJpegQuality";
- this.trackBarJpegQuality.Size = new System.Drawing.Size(233, 45);
- this.trackBarJpegQuality.TabIndex = 3;
- this.trackBarJpegQuality.TickFrequency = 10;
- this.trackBarJpegQuality.Scroll += new System.EventHandler(this.TrackBarJpegQualityScroll);
- //
- // checkbox_dontaskagain
- //
- this.checkbox_dontaskagain.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
- this.checkbox_dontaskagain.ImageAlign = System.Drawing.ContentAlignment.TopLeft;
- this.checkbox_dontaskagain.Location = new System.Drawing.Point(12, 106);
- this.checkbox_dontaskagain.Name = "checkbox_dontaskagain";
- this.checkbox_dontaskagain.LanguageKey = "qualitydialog_dontaskagain";
- this.checkbox_dontaskagain.Size = new System.Drawing.Size(268, 37);
- this.checkbox_dontaskagain.TabIndex = 5;
- this.checkbox_dontaskagain.TextAlign = System.Drawing.ContentAlignment.TopLeft;
- this.checkbox_dontaskagain.UseVisualStyleBackColor = true;
- //
- // button_ok
- //
- this.button_ok.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.button_ok.Location = new System.Drawing.Point(205, 149);
- this.button_ok.Name = "button_ok";
- this.button_ok.Size = new System.Drawing.Size(75, 23);
- this.button_ok.TabIndex = 1;
- this.button_ok.LanguageKey = "OK";
- this.button_ok.UseVisualStyleBackColor = true;
- this.button_ok.Click += new System.EventHandler(this.Button_okClick);
- //
- // checkBox_reduceColors
- //
- this.checkBox_reduceColors.Location = new System.Drawing.Point(12, 11);
- this.checkBox_reduceColors.Name = "checkBox_reduceColors";
- this.checkBox_reduceColors.Size = new System.Drawing.Size(95, 17);
- this.checkBox_reduceColors.TabIndex = 2;
- this.checkBox_reduceColors.Text = "settings_reducecolors";
- this.checkBox_reduceColors.UseVisualStyleBackColor = true;
- //
- // QualityDialog
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(299, 184);
- this.ControlBox = false;
- this.Controls.Add(this.checkBox_reduceColors);
- this.Controls.Add(this.button_ok);
- this.Controls.Add(this.checkbox_dontaskagain);
- this.Controls.Add(this.label_choosejpegquality);
- this.Controls.Add(this.textBoxJpegQuality);
- this.Controls.Add(this.trackBarJpegQuality);
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "QualityDialog";
- this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
- this.LanguageKey = "qualitydialog_title";
- ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).EndInit();
- this.ResumeLayout(false);
- this.PerformLayout();
-
- }
- private GreenshotButton button_ok;
- private GreenshotCheckBox checkbox_dontaskagain;
- private System.Windows.Forms.TrackBar trackBarJpegQuality;
- private System.Windows.Forms.TextBox textBoxJpegQuality;
- private GreenshotLabel label_choosejpegquality;
- private System.Windows.Forms.CheckBox checkBox_reduceColors;
- }
-}
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace GreenshotPlugin.Controls {
+ partial class QualityDialog {
+ ///
+ /// Designer variable used to keep track of non-visual components.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Disposes resources used by the form.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing) {
+ if (components != null) {
+ components.Dispose();
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// This method is required for Windows Forms designer support.
+ /// Do not change the method contents inside the source code editor. The Forms designer might
+ /// not be able to load this method if it was changed manually.
+ ///
+ private void InitializeComponent()
+ {
+ this.label_choosejpegquality = new GreenshotPlugin.Controls.GreenshotLabel();
+ this.textBoxJpegQuality = new System.Windows.Forms.TextBox();
+ this.trackBarJpegQuality = new System.Windows.Forms.TrackBar();
+ this.checkbox_dontaskagain = new GreenshotPlugin.Controls.GreenshotCheckBox();
+ this.button_ok = new GreenshotPlugin.Controls.GreenshotButton();
+ this.checkBox_reduceColors = new System.Windows.Forms.CheckBox();
+ ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).BeginInit();
+ this.SuspendLayout();
+ //
+ // label_choosejpegquality
+ //
+ this.label_choosejpegquality.Location = new System.Drawing.Point(12, 47);
+ this.label_choosejpegquality.Name = "label_choosejpegquality";
+ this.label_choosejpegquality.Size = new System.Drawing.Size(268, 19);
+ this.label_choosejpegquality.TabIndex = 15;
+ this.label_choosejpegquality.LanguageKey = "jpegqualitydialog_choosejpegquality";
+ //
+ // textBoxJpegQuality
+ //
+ this.textBoxJpegQuality.Location = new System.Drawing.Point(245, 69);
+ this.textBoxJpegQuality.Name = "textBoxJpegQuality";
+ this.textBoxJpegQuality.ReadOnly = true;
+ this.textBoxJpegQuality.Size = new System.Drawing.Size(35, 20);
+ this.textBoxJpegQuality.TabIndex = 4;
+ this.textBoxJpegQuality.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
+ //
+ // trackBarJpegQuality
+ //
+ this.trackBarJpegQuality.LargeChange = 10;
+ this.trackBarJpegQuality.Location = new System.Drawing.Point(12, 69);
+ this.trackBarJpegQuality.Maximum = 100;
+ this.trackBarJpegQuality.Name = "trackBarJpegQuality";
+ this.trackBarJpegQuality.Size = new System.Drawing.Size(233, 45);
+ this.trackBarJpegQuality.TabIndex = 3;
+ this.trackBarJpegQuality.TickFrequency = 10;
+ this.trackBarJpegQuality.Scroll += new System.EventHandler(this.TrackBarJpegQualityScroll);
+ //
+ // checkbox_dontaskagain
+ //
+ this.checkbox_dontaskagain.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
+ this.checkbox_dontaskagain.ImageAlign = System.Drawing.ContentAlignment.TopLeft;
+ this.checkbox_dontaskagain.Location = new System.Drawing.Point(12, 106);
+ this.checkbox_dontaskagain.Name = "checkbox_dontaskagain";
+ this.checkbox_dontaskagain.LanguageKey = "qualitydialog_dontaskagain";
+ this.checkbox_dontaskagain.Size = new System.Drawing.Size(268, 37);
+ this.checkbox_dontaskagain.TabIndex = 5;
+ this.checkbox_dontaskagain.TextAlign = System.Drawing.ContentAlignment.TopLeft;
+ this.checkbox_dontaskagain.UseVisualStyleBackColor = true;
+ //
+ // button_ok
+ //
+ this.button_ok.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.button_ok.Location = new System.Drawing.Point(205, 149);
+ this.button_ok.Name = "button_ok";
+ this.button_ok.Size = new System.Drawing.Size(75, 23);
+ this.button_ok.TabIndex = 1;
+ this.button_ok.LanguageKey = "OK";
+ this.button_ok.UseVisualStyleBackColor = true;
+ this.button_ok.Click += new System.EventHandler(this.Button_okClick);
+ //
+ // checkBox_reduceColors
+ //
+ this.checkBox_reduceColors.AutoSize = true;
+ this.checkBox_reduceColors.Location = new System.Drawing.Point(12, 11);
+ this.checkBox_reduceColors.Name = "checkBox_reduceColors";
+ this.checkBox_reduceColors.Size = new System.Drawing.Size(95, 17);
+ this.checkBox_reduceColors.TabIndex = 2;
+ this.checkBox_reduceColors.Text = "settings_reducecolors";
+ this.checkBox_reduceColors.UseVisualStyleBackColor = true;
+ //
+ // QualityDialog
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.ClientSize = new System.Drawing.Size(299, 184);
+ this.ControlBox = false;
+ this.Controls.Add(this.checkBox_reduceColors);
+ this.Controls.Add(this.button_ok);
+ this.Controls.Add(this.checkbox_dontaskagain);
+ this.Controls.Add(this.label_choosejpegquality);
+ this.Controls.Add(this.textBoxJpegQuality);
+ this.Controls.Add(this.trackBarJpegQuality);
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "QualityDialog";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.LanguageKey = "qualitydialog_title";
+ ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+ private GreenshotPlugin.Controls.GreenshotButton button_ok;
+ private GreenshotPlugin.Controls.GreenshotCheckBox checkbox_dontaskagain;
+ private System.Windows.Forms.TrackBar trackBarJpegQuality;
+ private System.Windows.Forms.TextBox textBoxJpegQuality;
+ private GreenshotPlugin.Controls.GreenshotLabel label_choosejpegquality;
+ private System.Windows.Forms.CheckBox checkBox_reduceColors;
+ }
+}
diff --git a/GreenshotPlugin/Controls/QualityDialog.cs b/GreenshotPlugin/Controls/QualityDialog.cs
new file mode 100644
index 000000000..ada7ad476
--- /dev/null
+++ b/GreenshotPlugin/Controls/QualityDialog.cs
@@ -0,0 +1,67 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces.Plugin;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// Description of JpegQualityDialog.
+ ///
+ public partial class QualityDialog : GreenshotForm {
+ private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
+ public SurfaceOutputSettings Settings {
+ get;
+ set;
+ }
+
+ public QualityDialog(SurfaceOutputSettings outputSettings) {
+ Settings = outputSettings;
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+
+ checkBox_reduceColors.Checked = Settings.ReduceColors;
+ trackBarJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format);
+ trackBarJpegQuality.Value = Settings.JPGQuality;
+ textBoxJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format);
+ textBoxJpegQuality.Text = Settings.JPGQuality.ToString();
+ ToFront = true;
+ }
+
+ private void Button_okClick(object sender, EventArgs e) {
+ Settings.JPGQuality = trackBarJpegQuality.Value;
+ Settings.ReduceColors = checkBox_reduceColors.Checked;
+ if (checkbox_dontaskagain.Checked) {
+ conf.OutputFileJpegQuality = Settings.JPGQuality;
+ conf.OutputFilePromptQuality = false;
+ conf.OutputFileReduceColors = Settings.ReduceColors;
+ IniConfig.Save();
+ }
+ }
+
+ private void TrackBarJpegQualityScroll(object sender, EventArgs e) {
+ textBoxJpegQuality.Text = trackBarJpegQuality.Value.ToString();
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/SaveImageFileDialog.cs b/GreenshotPlugin/Controls/SaveImageFileDialog.cs
new file mode 100644
index 000000000..45f98118f
--- /dev/null
+++ b/GreenshotPlugin/Controls/SaveImageFileDialog.cs
@@ -0,0 +1,203 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.IO;
+using System.Windows.Forms;
+using GreenshotPlugin.Core;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using log4net;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// Custom dialog for saving images, wraps SaveFileDialog.
+ /// For some reason SFD is sealed :(
+ ///
+ public class SaveImageFileDialog : IDisposable {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(SaveImageFileDialog));
+ private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
+ protected SaveFileDialog SaveFileDialog;
+ private FilterOption[] _filterOptions;
+ private DirectoryInfo _eagerlyCreatedDirectory;
+ private readonly ICaptureDetails _captureDetails;
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ if (SaveFileDialog != null) {
+ SaveFileDialog.Dispose();
+ SaveFileDialog = null;
+ }
+ }
+ }
+
+ public SaveImageFileDialog() {
+ Init();
+ }
+
+ public SaveImageFileDialog(ICaptureDetails captureDetails) {
+ _captureDetails = captureDetails;
+ Init();
+ }
+
+ private void Init() {
+ SaveFileDialog = new SaveFileDialog();
+ ApplyFilterOptions();
+ string initialDirectory = null;
+ try {
+ conf.ValidateAndCorrectOutputFileAsFullpath();
+ initialDirectory = Path.GetDirectoryName(conf.OutputFileAsFullpath);
+ } catch {
+ LOG.WarnFormat("OutputFileAsFullpath was set to {0}, ignoring due to problem in path.", conf.OutputFileAsFullpath);
+ }
+
+ if (!string.IsNullOrEmpty(initialDirectory) && Directory.Exists(initialDirectory)) {
+ SaveFileDialog.InitialDirectory = initialDirectory;
+ } else if (Directory.Exists(conf.OutputFilePath)) {
+ SaveFileDialog.InitialDirectory = conf.OutputFilePath;
+ }
+ // The following property fixes a problem that the directory where we save is locked (bug #2899790)
+ SaveFileDialog.RestoreDirectory = true;
+ SaveFileDialog.OverwritePrompt = true;
+ SaveFileDialog.CheckPathExists = false;
+ SaveFileDialog.AddExtension = true;
+ ApplySuggestedValues();
+ }
+
+ private void ApplyFilterOptions() {
+ PrepareFilterOptions();
+ string fdf = string.Empty;
+ int preselect = 0;
+ var outputFileFormatAsString = Enum.GetName(typeof(OutputFormat), conf.OutputFileFormat);
+ for(int i=0; i<_filterOptions.Length; i++){
+ FilterOption fo = _filterOptions[i];
+ fdf += fo.Label + "|*." + fo.Extension + "|";
+ if(outputFileFormatAsString == fo.Extension)
+ preselect = i;
+ }
+ fdf = fdf.Substring(0, fdf.Length-1);
+ SaveFileDialog.Filter = fdf;
+ SaveFileDialog.FilterIndex = preselect + 1;
+ }
+
+ private void PrepareFilterOptions() {
+ OutputFormat[] supportedImageFormats = (OutputFormat[])Enum.GetValues(typeof(OutputFormat));
+ _filterOptions = new FilterOption[supportedImageFormats.Length];
+ for(int i=0; i<_filterOptions.Length; i++){
+ string ifo = supportedImageFormats[i].ToString();
+ if (ifo.ToLower().Equals("jpeg")) ifo = "Jpg"; // we dont want no jpeg files, so let the dialog check for jpg
+ FilterOption fo = new FilterOption
+ {
+ Label = ifo.ToUpper(),
+ Extension = ifo.ToLower()
+ };
+ _filterOptions.SetValue(fo, i);
+ }
+ }
+
+ ///
+ /// filename exactly as typed in the filename field
+ ///
+ public string FileName {
+ get {return SaveFileDialog.FileName;}
+ set {SaveFileDialog.FileName = value;}
+ }
+
+ ///
+ /// initial directory of the dialog
+ ///
+ public string InitialDirectory {
+ get {return SaveFileDialog.InitialDirectory;}
+ set {SaveFileDialog.InitialDirectory = value;}
+ }
+
+ ///
+ /// returns filename as typed in the filename field with extension.
+ /// if filename field value ends with selected extension, the value is just returned.
+ /// otherwise, the selected extension is appended to the filename.
+ ///
+ public string FileNameWithExtension {
+ get {
+ string fn = SaveFileDialog.FileName;
+ // if the filename contains a valid extension, which is the same like the selected filter item's extension, the filename is okay
+ if(fn.EndsWith(Extension,StringComparison.CurrentCultureIgnoreCase)) return fn;
+ // otherwise we just add the selected filter item's extension
+ else return fn + "." + Extension;
+ }
+ set {
+ FileName = Path.GetFileNameWithoutExtension(value);
+ Extension = Path.GetExtension(value);
+ }
+ }
+
+ ///
+ /// gets or sets selected extension
+ ///
+ public string Extension {
+ get {
+ return _filterOptions[SaveFileDialog.FilterIndex-1].Extension;
+ }
+ set {
+ for(int i=0; i<_filterOptions.Length; i++) {
+ if(value.Equals(_filterOptions[i].Extension, StringComparison.CurrentCultureIgnoreCase)) {
+ SaveFileDialog.FilterIndex = i + 1;
+ }
+ }
+ }
+ }
+
+ public DialogResult ShowDialog() {
+ DialogResult ret = SaveFileDialog.ShowDialog();
+ CleanUp();
+ return ret;
+ }
+
+ ///
+ /// sets InitialDirectory and FileName property of a SaveFileDialog smartly, considering default pattern and last used path
+ ///
+ private void ApplySuggestedValues() {
+ // build the full path and set dialog properties
+ FileName = FilenameHelper.GetFilenameWithoutExtensionFromPattern(conf.OutputFileFilenamePattern, _captureDetails);
+ }
+
+ private class FilterOption {
+ public string Label;
+ public string Extension;
+ }
+
+ private void CleanUp() {
+ // fix for bug #3379053
+ try {
+ if(_eagerlyCreatedDirectory != null && _eagerlyCreatedDirectory.GetFiles().Length == 0 && _eagerlyCreatedDirectory.GetDirectories().Length == 0) {
+ _eagerlyCreatedDirectory.Delete();
+ _eagerlyCreatedDirectory = null;
+ }
+ } catch (Exception e) {
+ LOG.WarnFormat("Couldn't cleanup directory due to: {0}", e.Message);
+ _eagerlyCreatedDirectory = null;
+ }
+ }
+ }
+}
diff --git a/GreenshotPlugin/Controls/ThumbnailForm.cs b/GreenshotPlugin/Controls/ThumbnailForm.cs
new file mode 100644
index 000000000..3e8256cf3
--- /dev/null
+++ b/GreenshotPlugin/Controls/ThumbnailForm.cs
@@ -0,0 +1,120 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Windows.Forms;
+using GreenshotPlugin.Core;
+using System.Drawing;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.UnmanagedHelpers;
+using GreenshotPlugin.UnmanagedHelpers.Enums;
+using GreenshotPlugin.UnmanagedHelpers.Structs;
+
+namespace GreenshotPlugin.Controls {
+ ///
+ /// This form allows us to show a Thumbnail preview of a window near the context menu when selecting a window to capture.
+ /// Didn't make it completely "generic" yet, but at least most logic is in here so we don't have it in the mainform.
+ ///
+ public class ThumbnailForm : FormWithoutActivation {
+ private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
+
+ private IntPtr _thumbnailHandle = IntPtr.Zero;
+
+ public ThumbnailForm() {
+ ShowInTaskbar = false;
+ FormBorderStyle = FormBorderStyle.None;
+ TopMost = false;
+ Enabled = false;
+ if (conf.WindowCaptureMode == WindowCaptureMode.Auto || conf.WindowCaptureMode == WindowCaptureMode.Aero) {
+ BackColor = Color.FromArgb(255, conf.DWMBackgroundColor.R, conf.DWMBackgroundColor.G, conf.DWMBackgroundColor.B);
+ } else {
+ BackColor = Color.White;
+ }
+
+ // cleanup at close
+ FormClosing += delegate {
+ UnregisterThumbnail();
+ };
+ }
+
+ public new void Hide() {
+ UnregisterThumbnail();
+ base.Hide();
+ }
+
+ private void UnregisterThumbnail() {
+ if (_thumbnailHandle != IntPtr.Zero) {
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ _thumbnailHandle = IntPtr.Zero;
+ }
+ }
+
+ ///
+ /// Show the thumbnail of the supplied window above (or under) the parent Control
+ ///
+ /// WindowDetails
+ /// Control
+ public void ShowThumbnail(WindowDetails window, Control parentControl) {
+ UnregisterThumbnail();
+
+ DWM.DwmRegisterThumbnail(Handle, window.Handle, out _thumbnailHandle);
+ if (_thumbnailHandle != IntPtr.Zero) {
+ DWM.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize);
+ int thumbnailHeight = 200;
+ int thumbnailWidth = (int)(thumbnailHeight * (sourceSize.Width / (float)sourceSize.Height));
+ if (parentControl != null && thumbnailWidth > parentControl.Width) {
+ thumbnailWidth = parentControl.Width;
+ thumbnailHeight = (int)(thumbnailWidth * (sourceSize.Height / (float)sourceSize.Width));
+ }
+ Width = thumbnailWidth;
+ Height = thumbnailHeight;
+ // Prepare the displaying of the Thumbnail
+ DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES
+ {
+ Opacity = 255,
+ Visible = true,
+ SourceClientAreaOnly = false,
+ Destination = new RECT(0, 0, thumbnailWidth, thumbnailHeight)
+ };
+ DWM.DwmUpdateThumbnailProperties(_thumbnailHandle, ref props);
+ if (parentControl != null) {
+ AlignToControl(parentControl);
+ }
+
+ if (!Visible) {
+ Show();
+ }
+ // Make sure it's on "top"!
+ if (parentControl != null) {
+ User32.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE);
+ }
+ }
+ }
+
+ public void AlignToControl(Control alignTo) {
+ Rectangle screenBounds = WindowCapture.GetScreenBounds();
+ if (screenBounds.Contains(alignTo.Left, alignTo.Top - Height)) {
+ Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Top - Height);
+ } else {
+ Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Bottom);
+ }
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/AbstractDestination.cs b/GreenshotPlugin/Core/AbstractDestination.cs
new file mode 100644
index 000000000..886dd5dab
--- /dev/null
+++ b/GreenshotPlugin/Core/AbstractDestination.cs
@@ -0,0 +1,341 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Threading;
+using System.Windows.Forms;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.UnmanagedHelpers;
+using log4net;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Description of AbstractDestination.
+ ///
+ public abstract class AbstractDestination : IDestination {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(AbstractDestination));
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+
+ public virtual int CompareTo(object obj) {
+ if (!(obj is IDestination other)) {
+ return 1;
+ }
+ if (Priority == other.Priority) {
+ return string.Compare(Description, other.Description, StringComparison.Ordinal);
+ }
+ return Priority - other.Priority;
+ }
+
+ public abstract string Designation {
+ get;
+ }
+
+ public abstract string Description {
+ get;
+ }
+
+ public virtual int Priority => 10;
+
+ public virtual Image DisplayIcon => null;
+
+ public virtual Keys EditorShortcutKeys => Keys.None;
+
+ public virtual IEnumerable DynamicDestinations() {
+ yield break;
+ }
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing) {
+ //if (disposing) {}
+ }
+
+ public virtual bool IsDynamic => false;
+
+ public virtual bool UseDynamicsOnly => false;
+
+ public virtual bool IsLinkable => false;
+
+ public virtual bool IsActive {
+ get {
+ if (CoreConfig.ExcludeDestinations != null && CoreConfig.ExcludeDestinations.Contains(Designation)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public abstract ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails);
+
+ ///
+ /// A small helper method to perform some default destination actions, like inform the surface of the export
+ ///
+ ///
+ ///
+ public void ProcessExport(ExportInformation exportInformation, ISurface surface) {
+ if (exportInformation != null && exportInformation.ExportMade) {
+ if (!string.IsNullOrEmpty(exportInformation.Uri)) {
+ surface.UploadUrl = exportInformation.Uri;
+ surface.SendMessageEvent(this, SurfaceMessageTyp.UploadedUri, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
+ } else if (!string.IsNullOrEmpty(exportInformation.Filepath)) {
+ surface.LastSaveFullPath = exportInformation.Filepath;
+ surface.SendMessageEvent(this, SurfaceMessageTyp.FileSaved, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
+ } else {
+ surface.SendMessageEvent(this, SurfaceMessageTyp.Info, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
+ }
+ surface.Modified = false;
+ } else if (!string.IsNullOrEmpty(exportInformation?.ErrorMessage)) {
+ surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("exported_to_error", exportInformation.DestinationDescription) + " " + exportInformation.ErrorMessage);
+ }
+ }
+
+ public override string ToString() {
+ return Description;
+ }
+
+ ///
+ /// Helper method to add events which set the tag, this way we can see why there might be a close.
+ ///
+ /// Item to add the events to
+ /// Menu to set the tag
+ /// Value for the tag
+ private void AddTagEvents(ToolStripMenuItem menuItem, ContextMenuStrip menu, string tagValue) {
+ if (menuItem != null && menu != null) {
+ menuItem.MouseDown += delegate
+ {
+ Log.DebugFormat("Setting tag to '{0}'", tagValue);
+ menu.Tag = tagValue;
+ };
+ menuItem.MouseUp += delegate
+ {
+ Log.Debug("Deleting tag");
+ menu.Tag = null;
+ };
+ }
+ }
+
+ ///
+ /// This method will create and show the destination picker menu
+ ///
+ /// Boolean if the dynamic values also need to be added
+ /// The surface which can be exported
+ /// Details for the surface
+ /// The list of destinations to show
+ ///
+ public ExportInformation ShowPickerMenu(bool addDynamics, ISurface surface, ICaptureDetails captureDetails, IEnumerable destinations) {
+ // Generate an empty ExportInformation object, for when nothing was selected.
+ ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker"));
+ var menu = new ContextMenuStrip
+ {
+ ImageScalingSize = CoreConfig.ScaledIconSize,
+ Tag = null,
+ TopLevel = true
+ };
+
+ menu.Opened += (sender, args) =>
+ {
+ var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, DpiHelper.GetDpi(menu.Handle));
+ menu.ImageScalingSize = scaledIconSize;
+ };
+
+ menu.Closing += delegate(object source, ToolStripDropDownClosingEventArgs eventArgs) {
+ Log.DebugFormat("Close reason: {0}", eventArgs.CloseReason);
+ switch (eventArgs.CloseReason) {
+ case ToolStripDropDownCloseReason.AppFocusChange:
+ if (menu.Tag == null) {
+ // Do not allow the close if the tag is not set, this means the user clicked somewhere else.
+ eventArgs.Cancel = true;
+ } else {
+ Log.DebugFormat("Letting the menu 'close' as the tag is set to '{0}'", menu.Tag);
+ }
+ break;
+ case ToolStripDropDownCloseReason.ItemClicked:
+ case ToolStripDropDownCloseReason.CloseCalled:
+ // The ContextMenuStrip can be "closed" for these reasons.
+ break;
+ case ToolStripDropDownCloseReason.Keyboard:
+ // Dispose as the close is clicked
+ if (!captureDetails.HasDestination("Editor")) {
+ surface.Dispose();
+ surface = null;
+ }
+ break;
+ default:
+ eventArgs.Cancel = true;
+ break;
+ }
+ };
+ menu.MouseEnter += delegate
+ {
+ // in case the menu has been unfocused, focus again so that dropdown menus will still open on mouseenter
+ if(!menu.ContainsFocus) menu.Focus();
+ };
+ foreach (IDestination destination in destinations) {
+ // Fix foreach loop variable for the delegate
+ ToolStripMenuItem item = destination.GetMenuItem(addDynamics, menu,
+ delegate(object sender, EventArgs e) {
+ ToolStripMenuItem toolStripMenuItem = sender as ToolStripMenuItem;
+ IDestination clickedDestination = (IDestination) toolStripMenuItem?.Tag;
+ if (clickedDestination == null) {
+ return;
+ }
+ menu.Tag = clickedDestination.Designation;
+ // Export
+ exportInformation = clickedDestination.ExportCapture(true, surface, captureDetails);
+ if (exportInformation != null && exportInformation.ExportMade) {
+ Log.InfoFormat("Export to {0} success, closing menu", exportInformation.DestinationDescription);
+ // close menu if the destination wasn't the editor
+ menu.Close();
+
+ // Cleanup surface, only if there is no editor in the destinations and we didn't export to the editor
+ if (!captureDetails.HasDestination("Editor") && !"Editor".Equals(clickedDestination.Designation)) {
+ surface.Dispose();
+ surface = null;
+ }
+ } else {
+ Log.Info("Export cancelled or failed, showing menu again");
+
+ // Make sure a click besides the menu don't close it.
+ menu.Tag = null;
+
+ // This prevents the problem that the context menu shows in the task-bar
+ ShowMenuAtCursor(menu);
+ }
+ }
+ );
+ if (item != null) {
+ menu.Items.Add(item);
+ }
+ }
+ // Close
+ menu.Items.Add(new ToolStripSeparator());
+ ToolStripMenuItem closeItem = new ToolStripMenuItem(Language.GetString("editor_close"))
+ {
+ Image = GreenshotResources.GetImage("Close.Image")
+ };
+ closeItem.Click += delegate {
+ // This menu entry is the close itself, we can dispose the surface
+ menu.Close();
+ if (!captureDetails.HasDestination("Editor")) {
+ surface.Dispose();
+ surface = null;
+ }
+ };
+ menu.Items.Add(closeItem);
+
+ ShowMenuAtCursor(menu);
+ return exportInformation;
+ }
+
+ ///
+ /// This method will show the supplied context menu at the mouse cursor, also makes sure it has focus and it's not visible in the taskbar.
+ ///
+ ///
+ private static void ShowMenuAtCursor(ContextMenuStrip menu) {
+ // find a suitable location
+ Point location = Cursor.Position;
+ Rectangle menuRectangle = new Rectangle(location, menu.Size);
+
+ menuRectangle.Intersect(WindowCapture.GetScreenBounds());
+ if (menuRectangle.Height < menu.Height) {
+ location.Offset(-40, -(menuRectangle.Height - menu.Height));
+ } else {
+ location.Offset(-40, -10);
+ }
+ // This prevents the problem that the context menu shows in the task-bar
+ User32.SetForegroundWindow(SimpleServiceProvider.Current.GetInstance().ContextMenuStrip.Handle);
+ menu.Show(location);
+ menu.Focus();
+
+ // Wait for the menu to close, so we can dispose it.
+ while (true) {
+ if (menu.Visible) {
+ Application.DoEvents();
+ Thread.Sleep(100);
+ } else {
+ menu.Dispose();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Return a menu item
+ ///
+ ///
+ ///
+ ///
+ /// ToolStripMenuItem
+ public virtual ToolStripMenuItem GetMenuItem(bool addDynamics, ContextMenuStrip menu, EventHandler destinationClickHandler) {
+ var basisMenuItem = new ToolStripMenuItem(Description)
+ {
+ Image = DisplayIcon,
+ Tag = this,
+ Text = Description
+ };
+ AddTagEvents(basisMenuItem, menu, Description);
+ basisMenuItem.Click -= destinationClickHandler;
+ basisMenuItem.Click += destinationClickHandler;
+
+ if (IsDynamic && addDynamics) {
+ basisMenuItem.DropDownOpening += delegate
+ {
+ if (basisMenuItem.DropDownItems.Count == 0) {
+ List subDestinations = new List();
+ // Fixing Bug #3536968 by catching the COMException (every exception) and not displaying the "subDestinations"
+ try {
+ subDestinations.AddRange(DynamicDestinations());
+ } catch (Exception ex) {
+ Log.ErrorFormat("Skipping {0}, due to the following error: {1}", Description, ex.Message);
+ }
+ if (subDestinations.Count > 0) {
+ if (UseDynamicsOnly && subDestinations.Count == 1) {
+ basisMenuItem.Tag = subDestinations[0];
+ basisMenuItem.Text = subDestinations[0].Description;
+ basisMenuItem.Click -= destinationClickHandler;
+ basisMenuItem.Click += destinationClickHandler;
+ } else {
+ foreach (IDestination subDestination in subDestinations) {
+ var destinationMenuItem = new ToolStripMenuItem(subDestination.Description)
+ {
+ Tag = subDestination,
+ Image = subDestination.DisplayIcon
+ };
+ destinationMenuItem.Click += destinationClickHandler;
+ AddTagEvents(destinationMenuItem, menu, subDestination.Description);
+ basisMenuItem.DropDownItems.Add(destinationMenuItem);
+ }
+ }
+ }
+ }
+ };
+ }
+
+ return basisMenuItem;
+ }
+
+ }
+}
diff --git a/GreenshotPlugin/Core/AbstractProcessor.cs b/GreenshotPlugin/Core/AbstractProcessor.cs
new file mode 100644
index 000000000..5e82b1d23
--- /dev/null
+++ b/GreenshotPlugin/Core/AbstractProcessor.cs
@@ -0,0 +1,63 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using GreenshotPlugin.Interfaces;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Description of AbstractProcessor.
+ ///
+ public abstract class AbstractProcessor : IProcessor {
+
+ public virtual int CompareTo(object obj) {
+ if (!(obj is IProcessor other)) {
+ return 1;
+ }
+ if (Priority == other.Priority) {
+ return Description.CompareTo(other.Description);
+ }
+ return Priority - other.Priority;
+ }
+
+ public abstract string Designation {
+ get;
+ }
+
+ public abstract string Description {
+ get;
+ }
+
+ public virtual int Priority => 10;
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing) {
+ //if (disposing) {}
+ }
+
+ public virtual bool isActive => true;
+
+ public abstract bool ProcessCapture(ISurface surface, ICaptureDetails captureDetails);
+ }
+}
diff --git a/GreenshotPlugin/Core/AccessibleHelper.cs b/GreenshotPlugin/Core/AccessibleHelper.cs
new file mode 100644
index 000000000..f6d5770b3
--- /dev/null
+++ b/GreenshotPlugin/Core/AccessibleHelper.cs
@@ -0,0 +1,276 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Accessibility;
+
+namespace GreenshotPlugin.Core {
+
+ ///
+ /// See: http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/03a8c835-e9e4-405b-8345-6c3d36bc8941
+ /// This should really be cleaned up, there is little OO behind this class!
+ /// Maybe move the basic Accessible functions to WindowDetails!?
+ ///
+ public class Accessible {
+ private static int AccessibleObjectFromWindow(IntPtr hWnd, OBJID idObject, ref IAccessible acc) {
+ var guid = new Guid("{618736e0-3c3d-11cf-810c-00aa00389b71}"); // IAccessible
+ object obj = null;
+ int num = AccessibleObjectFromWindow(hWnd, (uint)idObject, ref guid, ref obj);
+ acc = (IAccessible)obj;
+ return num;
+ }
+ [DllImport("oleacc.dll")]
+ private static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint id, ref Guid iid, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);
+ [DllImport("oleacc.dll")]
+ private static extern int AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] object[] rgvarChildren, out int pcObtained);
+
+ [DllImport("oleacc.dll", PreserveSig=false)]
+ [return: MarshalAs(UnmanagedType.Interface)]
+ public static extern object ObjectFromLresult(UIntPtr lResult, [MarshalAs(UnmanagedType.LPStruct)] Guid refiid, IntPtr wParam);
+
+ private enum OBJID : uint {
+ OBJID_WINDOW = 0x00000000,
+ }
+
+ private const int IE_ACTIVE_TAB = 2097154;
+ private const int CHILDID_SELF = 0;
+ private readonly IAccessible accessible;
+ private Accessible[] Children {
+ get {
+ object[] res = GetAccessibleChildren(accessible, out var num);
+ if (res == null) {
+ return new Accessible[0];
+ }
+
+ List list = new List(res.Length);
+ foreach (object obj in res) {
+ if (obj is IAccessible acc) {
+ list.Add(new Accessible(acc));
+ }
+ }
+ return list.ToArray();
+ }
+ }
+
+ private string Name {
+ get {
+ return accessible.get_accName(CHILDID_SELF);
+ }
+ }
+
+ private int ChildCount {
+ get {
+ return accessible.accChildCount;
+ }
+ }
+
+ public Accessible(IntPtr hWnd) {
+ AccessibleObjectFromWindow(hWnd, OBJID.OBJID_WINDOW, ref accessible);
+ if (accessible == null) {
+ throw new Exception();
+ }
+ }
+
+ public void ActivateIETab(string tabCaptionToActivate) {
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ if (tab.Name == tabCaptionToActivate) {
+ tab.Activate();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ public void CloseIETab(string tabCaptionToClose) {
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ if (tab.Name == tabCaptionToClose) {
+ foreach (var CloseTab in tab.Children) {
+ CloseTab.Activate();
+ }
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ public void ActivateIETab(int tabIndexToActivate) {
+ var index = 0;
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ if (tabIndexToActivate >= child.ChildCount -1) {
+ return;
+ }
+
+ if (index == tabIndexToActivate) {
+ tab.Activate();
+ return;
+ }
+ index++;
+ }
+ }
+ }
+ }
+
+ public string IEActiveTabUrl {
+ get {
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ object tabIndex = tab.accessible.get_accState(CHILDID_SELF);
+
+ if ((int)tabIndex == IE_ACTIVE_TAB) {
+ var description = tab.accessible.get_accDescription(CHILDID_SELF);
+
+ if (!string.IsNullOrEmpty(description)) {
+ if (description.Contains(Environment.NewLine)) {
+ var url = description.Substring(description.IndexOf(Environment.NewLine)).Trim();
+ return url;
+ }
+ }
+ }
+ }
+ }
+ }
+ return string.Empty;
+ }
+ }
+
+ public int IEActiveTabIndex {
+ get {
+ var index = 0;
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ object tabIndex = tab.accessible.get_accState(0);
+
+ if ((int)tabIndex == IE_ACTIVE_TAB) {
+ return index;
+ }
+ index++;
+ }
+ }
+ }
+ return -1;
+ }
+ }
+
+ public string IEActiveTabCaption {
+ get {
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ object tabIndex = tab.accessible.get_accState(0);
+
+ if ((int)tabIndex == IE_ACTIVE_TAB) {
+ return tab.Name;
+ }
+ }
+ }
+ }
+ return string.Empty;
+ }
+ }
+
+ public List IETabCaptions {
+ get {
+ var captionList = new List();
+
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ captionList.Add(tab.Name);
+ }
+ }
+ }
+
+ // TODO: Why again?
+ if (captionList.Count > 0) {
+ captionList.RemoveAt(captionList.Count - 1);
+ }
+
+ return captionList;
+ }
+ }
+
+
+ public IEnumerable IETabUrls {
+ get {
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ object tabIndex = tab.accessible.get_accState(CHILDID_SELF);
+ var description = tab.accessible.get_accDescription(CHILDID_SELF);
+ if (!string.IsNullOrEmpty(description)) {
+ if (description.Contains(Environment.NewLine)) {
+ var url = description.Substring(description.IndexOf(Environment.NewLine)).Trim();
+ yield return url;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int IETabCount {
+ get {
+ foreach (Accessible accessor in Children) {
+ foreach (var child in accessor.Children) {
+ foreach (var tab in child.Children) {
+ return child.ChildCount - 1;
+ }
+ }
+ }
+ return 0;
+ }
+ }
+
+ private Accessible(IAccessible acc) {
+ accessible = acc ?? throw new Exception();
+ }
+
+ private void Activate() {
+ accessible.accDoDefaultAction(CHILDID_SELF);
+ }
+
+ private static object[] GetAccessibleChildren(IAccessible ao, out int childs) {
+ childs = 0;
+ object[] ret = null;
+ int count = ao.accChildCount;
+
+ if (count > 0) {
+ ret = new object[count];
+ AccessibleChildren(ao, 0, count, ret, out childs);
+ }
+ return ret;
+ }
+ }
+}
+
diff --git a/GreenshotPlugin/Core/AnimationHelpers.cs b/GreenshotPlugin/Core/AnimationHelpers.cs
new file mode 100644
index 000000000..cd96b890a
--- /dev/null
+++ b/GreenshotPlugin/Core/AnimationHelpers.cs
@@ -0,0 +1,547 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Drawing;
+using System.Collections.Generic;
+
+namespace GreenshotPlugin.Core {
+
+ ///
+ /// Helper interface for passing base type
+ ///
+ public interface IAnimator {
+ ///
+ /// Is there a next frame?
+ ///
+ bool HasNext {
+ get;
+ }
+ ///
+ /// The amount of frames
+ ///
+ int Frames {
+ get;
+ }
+
+ ///
+ /// Current frame number
+ ///
+ int CurrentFrameNr {
+ get;
+ }
+ }
+
+ ///
+ /// This class is used to store a animation leg
+ ///
+ internal class AnimationLeg {
+ public T Destination {
+ get;
+ set;
+ }
+
+ public int Frames {
+ get;
+ set;
+ }
+
+ public EasingType EasingType {
+ get;
+ set;
+ }
+
+ public EasingMode EasingMode {
+ get;
+ set;
+ }
+ }
+
+ ///
+ /// Base class for the animation logic, this only implements Properties and a constructor
+ ///
+ /// Type for the animation, like Point/Rectangle/Size
+ public abstract class AnimatorBase : IAnimator {
+ private readonly Queue> _queue = new Queue>();
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public AnimatorBase(T first, T last, int frames, EasingType easingType, EasingMode easingMode) {
+ First = first;
+ Last = last;
+ Frames = frames;
+ Current = first;
+ EasingType = easingType;
+ EasingMode = easingMode;
+ }
+
+ ///
+ /// The amount of frames
+ ///
+ public int Frames {
+ get;
+ private set;
+ }
+
+ ///
+ /// Current frame number
+ ///
+ public int CurrentFrameNr { get; private set; }
+
+ ///
+ /// First animation value
+ ///
+ public T First { get; private set; }
+
+ ///
+ /// Last animation value, of this "leg"
+ ///
+ public T Last { get; private set; }
+
+ ///
+ /// Final animation value, this is including the legs
+ ///
+ public T Final {
+ get {
+ if (_queue.Count == 0) {
+ return Last;
+ }
+ return _queue.ToArray()[_queue.Count - 1].Destination;
+ }
+ }
+
+ ///
+ /// This restarts the current animation and changes the last frame
+ ///
+ ///
+ public void ChangeDestination(T newDestination) {
+ ChangeDestination(newDestination, Frames);
+ }
+
+ ///
+ /// This restarts the current animation and changes the last frame
+ ///
+ ///
+ ///
+ public void ChangeDestination(T newDestination, int frames) {
+ _queue.Clear();
+ First = Current;
+ CurrentFrameNr = 0;
+ Frames = frames;
+ Last = newDestination;
+ }
+
+ ///
+ /// Queue the destination, it will be used to continue at the last frame
+ /// All values will stay the same
+ ///
+ ///
+ public void QueueDestinationLeg(T queuedDestination) {
+ QueueDestinationLeg(queuedDestination, Frames, EasingType, EasingMode);
+ }
+
+ ///
+ /// Queue the destination, it will be used to continue at the last frame
+ ///
+ ///
+ ///
+ public void QueueDestinationLeg(T queuedDestination, int frames) {
+ QueueDestinationLeg(queuedDestination, frames, EasingType, EasingMode);
+ }
+
+ ///
+ /// Queue the destination, it will be used to continue at the last frame
+ ///
+ ///
+ ///
+ /// EasingType
+ public void QueueDestinationLeg(T queuedDestination, int frames, EasingType easingType) {
+ QueueDestinationLeg(queuedDestination, frames, easingType, EasingMode);
+ }
+
+ ///
+ /// Queue the destination, it will be used to continue at the last frame
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void QueueDestinationLeg(T queuedDestination, int frames, EasingType easingType, EasingMode easingMode) {
+ AnimationLeg leg = new AnimationLeg
+ {
+ Destination = queuedDestination,
+ Frames = frames,
+ EasingType = easingType,
+ EasingMode = easingMode
+ };
+ _queue.Enqueue(leg);
+ }
+
+ ///
+ /// The EasingType to use for the animation
+ ///
+ public EasingType EasingType {
+ get;
+ set;
+ }
+
+ ///
+ /// The EasingMode to use for the animation
+ ///
+ public EasingMode EasingMode {
+ get;
+ set;
+ }
+
+ ///
+ /// Get the easing value, which is from 0-1 and depends on the frame
+ ///
+ protected double EasingValue {
+ get =>
+ EasingMode switch
+ {
+ EasingMode.EaseOut => Easing.EaseOut(CurrentFrameNr / (double) Frames, EasingType),
+ EasingMode.EaseInOut => Easing.EaseInOut(CurrentFrameNr / (double) Frames, EasingType),
+ EasingMode.EaseIn => Easing.EaseIn(CurrentFrameNr / (double) Frames, EasingType),
+ _ => Easing.EaseIn(CurrentFrameNr / (double) Frames, EasingType)
+ };
+ }
+
+ ///
+ /// Get the current (previous) frame object
+ ///
+ public virtual T Current { get; set; }
+
+ ///
+ /// Returns if there are any frame left, and if this is the case than the frame is increased.
+ ///
+ public virtual bool NextFrame {
+ get {
+ if (CurrentFrameNr < Frames) {
+ CurrentFrameNr++;
+ return true;
+ }
+ if (_queue.Count > 0) {
+ First = Current;
+ CurrentFrameNr = 0;
+ AnimationLeg nextLeg = _queue.Dequeue();
+ Last = nextLeg.Destination;
+ Frames = nextLeg.Frames;
+ EasingType = nextLeg.EasingType;
+ EasingMode = nextLeg.EasingMode;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ ///
+ /// Are there more frames to animate?
+ ///
+ public virtual bool HasNext {
+ get {
+ if (CurrentFrameNr < Frames) {
+ return true;
+ }
+ return _queue.Count > 0;
+ }
+ }
+
+ ///
+ /// Get the next animation frame value object
+ ///
+ ///
+ public abstract T Next();
+ }
+
+ ///
+ /// Implementation of the RectangleAnimator
+ ///
+ public class RectangleAnimator : AnimatorBase {
+ public RectangleAnimator(Rectangle first, Rectangle last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) {
+ }
+ public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn) {
+ }
+
+ public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode) {
+ }
+
+ ///
+ /// Calculate the next frame object
+ ///
+ /// Rectangle
+ public override Rectangle Next() {
+ if (NextFrame) {
+ double easingValue = EasingValue;
+ double dx = Last.X - First.X;
+ double dy = Last.Y - First.Y;
+
+ int x = First.X + (int)(easingValue * dx);
+ int y = First.Y + (int)(easingValue * dy);
+ double dw = Last.Width - First.Width;
+ double dh = Last.Height - First.Height;
+ int width = First.Width + (int)(easingValue * dw);
+ int height = First.Height + (int)(easingValue * dh);
+ Current = new Rectangle(x, y, width, height);
+ }
+ return Current;
+ }
+ }
+
+ ///
+ /// Implementation of the PointAnimator
+ ///
+ public class PointAnimator : AnimatorBase {
+ public PointAnimator(Point first, Point last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) {
+ }
+ public PointAnimator(Point first, Point last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn) {
+ }
+ public PointAnimator(Point first, Point last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode) {
+ }
+
+ ///
+ /// Calculate the next frame value
+ ///
+ /// Point
+ public override Point Next() {
+ if (NextFrame) {
+ double easingValue = EasingValue;
+ double dx = Last.X - First.X;
+ double dy = Last.Y - First.Y;
+
+ int x = First.X + (int)(easingValue * dx);
+ int y = First.Y + (int)(easingValue * dy);
+ Current = new Point(x, y);
+ }
+ return Current;
+ }
+ }
+
+ ///
+ /// Implementation of the SizeAnimator
+ ///
+ public class SizeAnimator : AnimatorBase {
+ public SizeAnimator(Size first, Size last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) {
+ }
+ public SizeAnimator(Size first, Size last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn) {
+ }
+ public SizeAnimator(Size first, Size last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode) {
+ }
+
+ ///
+ /// Calculate the next frame values
+ ///
+ /// Size
+ public override Size Next() {
+ if (NextFrame) {
+ double easingValue = EasingValue;
+ double dw = Last.Width - First.Width;
+ double dh = Last.Height - First.Height;
+ int width = First.Width + (int)(easingValue * dw);
+ int height = First.Height + (int)(easingValue * dh);
+ Current = new Size(width, height);
+ }
+ return Current;
+ }
+ }
+
+ ///
+ /// Implementation of the ColorAnimator
+ ///
+ public class ColorAnimator : AnimatorBase {
+ public ColorAnimator(Color first, Color last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) {
+ }
+ public ColorAnimator(Color first, Color last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn) {
+ }
+ public ColorAnimator(Color first, Color last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode) {
+ }
+
+ ///
+ /// Calculate the next frame values
+ ///
+ /// Color
+ public override Color Next() {
+ if (NextFrame) {
+ double easingValue = EasingValue;
+ double da = Last.A - First.A;
+ double dr = Last.R - First.R;
+ double dg = Last.G - First.G;
+ double db = Last.B - First.B;
+ int a = First.A + (int)(easingValue * da);
+ int r = First.R + (int)(easingValue * dr);
+ int g = First.G + (int)(easingValue * dg);
+ int b = First.B + (int)(easingValue * db);
+ Current = Color.FromArgb(a,r,g,b);
+ }
+ return Current;
+ }
+ }
+
+ ///
+ /// Implementation of the IntAnimator
+ ///
+ public class IntAnimator : AnimatorBase {
+ public IntAnimator(int first, int last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) {
+ }
+ public IntAnimator(int first, int last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn) {
+ }
+ public IntAnimator(int first, int last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode) {
+ }
+
+ ///
+ /// Calculate the next frame values
+ ///
+ /// int
+ public override int Next() {
+ if (NextFrame) {
+ double easingValue = EasingValue;
+ double delta = Last - First;
+ Current = First + (int)(easingValue * delta);
+ }
+ return Current;
+ }
+ }
+
+ ///
+ /// Easing logic, to make the animations more "fluent"
+ ///
+ public static class Easing {
+ // Adapted from http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf
+
+ public static double Ease(double linearStep, double acceleration, EasingType type) {
+ double easedStep = acceleration > 0 ? EaseIn(linearStep, type) : acceleration < 0 ? EaseOut(linearStep, type) : linearStep;
+ // Lerp:
+ return ((easedStep - linearStep) * Math.Abs(acceleration) + linearStep);
+ }
+
+ public static double EaseIn(double linearStep, EasingType type) =>
+ type switch
+ {
+ EasingType.Step => (linearStep < 0.5 ? 0 : 1),
+ EasingType.Linear => linearStep,
+ EasingType.Sine => Sine.EaseIn(linearStep),
+ EasingType.Quadratic => Power.EaseIn(linearStep, 2),
+ EasingType.Cubic => Power.EaseIn(linearStep, 3),
+ EasingType.Quartic => Power.EaseIn(linearStep, 4),
+ EasingType.Quintic => Power.EaseIn(linearStep, 5),
+ _ => throw new NotImplementedException()
+ };
+
+ public static double EaseOut(double linearStep, EasingType type) =>
+ type switch
+ {
+ EasingType.Step => (linearStep < 0.5 ? 0 : 1),
+ EasingType.Linear => linearStep,
+ EasingType.Sine => Sine.EaseOut(linearStep),
+ EasingType.Quadratic => Power.EaseOut(linearStep, 2),
+ EasingType.Cubic => Power.EaseOut(linearStep, 3),
+ EasingType.Quartic => Power.EaseOut(linearStep, 4),
+ EasingType.Quintic => Power.EaseOut(linearStep, 5),
+ _ => throw new NotImplementedException()
+ };
+
+ public static double EaseInOut(double linearStep, EasingType easeInType, EasingType easeOutType) {
+ return linearStep < 0.5 ? EaseInOut(linearStep, easeInType) : EaseInOut(linearStep, easeOutType);
+ }
+
+ public static double EaseInOut(double linearStep, EasingType type) =>
+ type switch
+ {
+ EasingType.Step => (linearStep < 0.5 ? 0 : 1),
+ EasingType.Linear => linearStep,
+ EasingType.Sine => Sine.EaseInOut(linearStep),
+ EasingType.Quadratic => Power.EaseInOut(linearStep, 2),
+ EasingType.Cubic => Power.EaseInOut(linearStep, 3),
+ EasingType.Quartic => Power.EaseInOut(linearStep, 4),
+ EasingType.Quintic => Power.EaseInOut(linearStep, 5),
+ _ => throw new NotImplementedException()
+ };
+
+ private static class Sine {
+ public static double EaseIn(double s) {
+ return Math.Sin(s * (Math.PI / 2) - (Math.PI / 2)) + 1;
+ }
+ public static double EaseOut(double s) {
+ return Math.Sin(s * (Math.PI / 2));
+ }
+ public static double EaseInOut(double s) {
+ return Math.Sin(s * Math.PI - (Math.PI / 2) + 1) / 2;
+ }
+ }
+
+ private static class Power {
+ public static double EaseIn(double s, int power) {
+ return Math.Pow(s, power);
+ }
+ public static double EaseOut(double s, int power) {
+ var sign = power % 2 == 0 ? -1 : 1;
+ return sign * (Math.Pow(s - 1, power) + sign);
+ }
+ public static double EaseInOut(double s, int power) {
+ s *= 2;
+ if (s < 1) {
+ return EaseIn(s, power) / 2;
+ }
+ var sign = power % 2 == 0 ? -1 : 1;
+ return (sign / 2.0 * (Math.Pow(s - 2, power) + sign * 2));
+ }
+ }
+ }
+
+ ///
+ /// This defines the way the animation works
+ ///
+ public enum EasingType {
+ Step,
+ Linear,
+ Sine,
+ Quadratic,
+ Cubic,
+ Quartic,
+ Quintic
+ }
+
+ public enum EasingMode {
+ EaseIn,
+ EaseOut,
+ EaseInOut
+ }
+}
diff --git a/GreenshotPlugin/Core/BinaryStructHelper.cs b/GreenshotPlugin/Core/BinaryStructHelper.cs
new file mode 100644
index 000000000..b8584c572
--- /dev/null
+++ b/GreenshotPlugin/Core/BinaryStructHelper.cs
@@ -0,0 +1,93 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Runtime.InteropServices;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// A helper class which does the mashalling for structs
+ ///
+ public static class BinaryStructHelper {
+ ///
+ /// Get a struct from a byte array
+ ///
+ /// typeof struct
+ /// byte[]
+ /// struct
+ public static T FromByteArray(byte[] bytes) where T : struct {
+ IntPtr ptr = IntPtr.Zero;
+ try {
+ int size = Marshal.SizeOf(typeof(T));
+ ptr = Marshal.AllocHGlobal(size);
+ Marshal.Copy(bytes, 0, ptr, size);
+ return FromIntPtr(ptr);
+ } finally {
+ if (ptr != IntPtr.Zero) {
+ Marshal.FreeHGlobal(ptr);
+ }
+ }
+ }
+
+ ///
+ /// Get a struct from a byte array
+ ///
+ /// typeof struct
+ /// Pointer to the structor to return
+ /// struct
+ public static T FromIntPtr(IntPtr intPtr) where T : struct {
+ object obj = Marshal.PtrToStructure(intPtr, typeof(T));
+ return (T)obj;
+ }
+
+ ///
+ /// copy a struct to a byte array
+ ///
+ /// typeof struct
+ /// struct
+ /// byte[]
+ public static byte[] ToByteArray(T obj) where T : struct {
+ IntPtr ptr = IntPtr.Zero;
+ try {
+ int size = Marshal.SizeOf(typeof(T));
+ ptr = Marshal.AllocHGlobal(size);
+ Marshal.StructureToPtr(obj, ptr, true);
+ return FromPtrToByteArray(ptr);
+ } finally {
+ if (ptr != IntPtr.Zero) {
+ Marshal.FreeHGlobal(ptr);
+ }
+ }
+ }
+
+ ///
+ /// copy a struct from a pointer to a byte array
+ ///
+ /// typeof struct
+ /// IntPtr to struct
+ /// byte[]
+ public static byte[] FromPtrToByteArray(IntPtr ptr) where T : struct {
+ int size = Marshal.SizeOf(typeof(T));
+ byte[] bytes = new byte[size];
+ Marshal.Copy(ptr, bytes, 0, size);
+ return bytes;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/Cache.cs b/GreenshotPlugin/Core/Cache.cs
new file mode 100644
index 000000000..ae41d7d60
--- /dev/null
+++ b/GreenshotPlugin/Core/Cache.cs
@@ -0,0 +1,214 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Timers;
+using log4net;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Cache class
+ ///
+ /// Type of key
+ /// Type of value
+ public class Cache {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(Cache));
+ private readonly IDictionary _internalCache = new Dictionary();
+ private readonly object _lockObject = new object();
+ private readonly int _secondsToExpire = 10;
+ private readonly CacheObjectExpired _expiredCallback;
+ public delegate void CacheObjectExpired(TK key, TV cacheValue);
+
+ ///
+ /// Initialize the cache
+ ///
+ public Cache() {
+ }
+ ///
+ /// Initialize the cache
+ ///
+ ///
+ public Cache(CacheObjectExpired expiredCallback) : this() {
+ _expiredCallback = expiredCallback;
+ }
+
+ ///
+ /// Initialize the cache with a expire setting
+ ///
+ ///
+ public Cache(int secondsToExpire) : this() {
+ _secondsToExpire = secondsToExpire;
+ }
+
+ ///
+ /// Initialize the cache with a expire setting
+ ///
+ ///
+ ///
+ public Cache(int secondsToExpire, CacheObjectExpired expiredCallback) : this(expiredCallback) {
+ _secondsToExpire = secondsToExpire;
+ }
+
+ ///
+ /// Enumerable for the values in the cache
+ ///
+ public IEnumerable Elements {
+ get {
+ List elements = new List();
+
+ lock (_lockObject)
+ {
+ foreach (TV element in _internalCache.Values) {
+ elements.Add(element);
+ }
+ }
+ foreach (TV element in elements) {
+ yield return element;
+ }
+ }
+ }
+
+ ///
+ /// Get the value by key from the cache
+ ///
+ ///
+ ///
+ public TV this[TK key] {
+ get {
+ TV result = default;
+ lock (_lockObject) {
+ if (_internalCache.ContainsKey(key)) {
+ result = _internalCache[key];
+ }
+ }
+ return result;
+ }
+ }
+
+ ///
+ /// Contains
+ ///
+ ///
+ /// true if the cache contains the key
+ public bool Contains(TK key)
+ {
+ lock (_lockObject)
+ {
+ return _internalCache.ContainsKey(key);
+ }
+ }
+
+ ///
+ /// Add a value to the cache
+ ///
+ ///
+ ///
+ public void Add(TK key, TV value) {
+ Add(key, value, null);
+ }
+
+ ///
+ /// Add a value to the cache
+ ///
+ ///
+ ///
+ /// optional value for the seconds to expire
+ public void Add(TK key, TV value, int? secondsToExpire) {
+ lock (_lockObject) {
+ var cachedItem = new CachedItem(key, value, secondsToExpire ?? _secondsToExpire);
+ cachedItem.Expired += delegate(TK cacheKey, TV cacheValue) {
+ if (_internalCache.ContainsKey(cacheKey)) {
+ Log.DebugFormat("Expiring object with Key: {0}", cacheKey);
+ _expiredCallback?.Invoke(cacheKey, cacheValue);
+ Remove(cacheKey);
+ } else {
+ Log.DebugFormat("Expired old object with Key: {0}", cacheKey);
+ }
+ };
+
+ if (_internalCache.ContainsKey(key)) {
+ _internalCache[key] = value;
+ Log.DebugFormat("Updated item with Key: {0}", key);
+ } else {
+ _internalCache.Add(key, cachedItem);
+ Log.DebugFormat("Added item with Key: {0}", key);
+ }
+ }
+ }
+
+ ///
+ /// Remove item from cache
+ ///
+ ///
+ public void Remove(TK key) {
+ lock (_lockObject) {
+ if (!_internalCache.ContainsKey(key)) {
+ throw new ApplicationException($"An object with key ‘{key}’ does not exists in cache");
+ }
+ _internalCache.Remove(key);
+ Log.DebugFormat("Removed item with Key: {0}", key);
+ }
+ }
+
+ ///
+ /// A cache item
+ ///
+ private class CachedItem {
+ public event CacheObjectExpired Expired;
+ private readonly int _secondsToExpire;
+ private readonly Timer _timerEvent;
+
+ public CachedItem(TK key, TV item, int secondsToExpire) {
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+ Key = key;
+ Item = item;
+ _secondsToExpire = secondsToExpire;
+ if (secondsToExpire <= 0)
+ {
+ return;
+ }
+ _timerEvent = new Timer(secondsToExpire * 1000) { AutoReset = false };
+ _timerEvent.Elapsed += timerEvent_Elapsed;
+ _timerEvent.Start();
+ }
+
+ private void ExpireNow() {
+ _timerEvent.Stop();
+ if (_secondsToExpire > 0) {
+ Expired?.Invoke(Key, Item);
+ }
+ }
+
+ private void timerEvent_Elapsed(object sender, ElapsedEventArgs e) {
+ ExpireNow();
+ }
+
+ public TK Key { get; private set; }
+ public TV Item { get; private set; }
+
+ public static implicit operator TV(CachedItem a) {
+ return a.Item;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Greenshot.Base/Core/Capture.cs b/GreenshotPlugin/Core/Capture.cs
similarity index 69%
rename from src/Greenshot.Base/Core/Capture.cs
rename to GreenshotPlugin/Core/Capture.cs
index 0be688727..95a7fb872 100644
--- a/src/Greenshot.Base/Core/Capture.cs
+++ b/GreenshotPlugin/Core/Capture.cs
@@ -1,119 +1,75 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: https://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
using System;
+using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
-using Dapplo.Windows.Common.Extensions;
-using Dapplo.Windows.Common.Structs;
-using Dapplo.Windows.User32;
-using Greenshot.Base.Interfaces;
-using Greenshot.Base.Interfaces.Ocr;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Ocr;
using log4net;
-namespace Greenshot.Base.Core
+namespace GreenshotPlugin.Core
{
///
/// This class is used to pass an instance of the "Capture" around
/// Having the Bitmap, eventually the Windows Title and cursor all together.
///
- public class Capture : ICapture
- {
+ public class Capture : ICapture {
private static readonly ILog Log = LogManager.GetLogger(typeof(Capture));
+ private List _elements = new List();
- private NativeRect _screenBounds;
-
+ private Rectangle _screenBounds;
///
/// Get/Set the screen bounds
///
- public NativeRect ScreenBounds
- {
- get
- {
- if (_screenBounds.IsEmpty)
- {
- _screenBounds = DisplayInfo.ScreenBounds;
+ public Rectangle ScreenBounds {
+ get {
+ if (_screenBounds == Rectangle.Empty) {
+ _screenBounds = WindowCapture.GetScreenBounds();
}
-
return _screenBounds;
}
set => _screenBounds = value;
}
private Image _image;
-
///
/// Get/Set the Image
///
- public Image Image
- {
+ public Image Image {
get => _image;
- set
- {
+ set {
_image?.Dispose();
_image = value;
- if (value != null)
- {
- if (value.PixelFormat.Equals(PixelFormat.Format8bppIndexed) || value.PixelFormat.Equals(PixelFormat.Format1bppIndexed) ||
- value.PixelFormat.Equals(PixelFormat.Format4bppIndexed))
- {
+ if (value != null) {
+ if (value.PixelFormat.Equals(PixelFormat.Format8bppIndexed) || value.PixelFormat.Equals(PixelFormat.Format1bppIndexed) || value.PixelFormat.Equals(PixelFormat.Format4bppIndexed)) {
Log.Debug("Converting Bitmap to PixelFormat.Format32bppArgb as we don't support: " + value.PixelFormat);
- try
- {
+ try {
// Default Bitmap PixelFormat is Format32bppArgb
_image = new Bitmap(value);
- }
- finally
- {
- // Always dispose, even when a exception occurred
+ } finally {
+ // Always dispose, even when a exception occured
value.Dispose();
}
}
-
Log.DebugFormat("Image is set with the following specifications: {0} - {1}", _image.Size, _image.PixelFormat);
- }
- else
- {
+ } else {
Log.Debug("Image is removed.");
}
}
}
- public void NullImage()
- {
+ public void NullImage() {
_image = null;
}
private Icon _cursor;
-
///
/// Get/Set the image for the Cursor
///
- public Icon Cursor
- {
+ public Icon Cursor {
get => _cursor;
- set
- {
+ set {
_cursor?.Dispose();
- _cursor = (Icon) value.Clone();
+ _cursor = (Icon)value.Clone();
}
}
@@ -127,45 +83,38 @@ namespace Greenshot.Base.Core
///
public bool CursorVisible { get; set; }
- private NativePoint _cursorLocation = NativePoint.Empty;
-
+ private Point _cursorLocation = Point.Empty;
///
/// Get/Set the CursorLocation
///
- public NativePoint CursorLocation
- {
+ public Point CursorLocation {
get => _cursorLocation;
set => _cursorLocation = value;
}
- private NativePoint _location = NativePoint.Empty;
-
+ private Point _location = Point.Empty;
///
/// Get/set the Location
///
- public NativePoint Location
- {
+ public Point Location {
get => _location;
set => _location = value;
}
private CaptureDetails _captureDetails;
-
///
/// Get/set the CaptureDetails
///
- public ICaptureDetails CaptureDetails
- {
+ public ICaptureDetails CaptureDetails {
get => _captureDetails;
- set => _captureDetails = (CaptureDetails) value;
+ set => _captureDetails = (CaptureDetails)value;
}
///
/// Default Constructor
///
- public Capture()
- {
- _screenBounds = DisplayInfo.ScreenBounds;
+ public Capture() {
+ _screenBounds = WindowCapture.GetScreenBounds();
_captureDetails = new CaptureDetails();
}
@@ -174,16 +123,14 @@ namespace Greenshot.Base.Core
/// Note: the supplied bitmap can be disposed immediately or when constructor is called.
///
/// Image
- public Capture(Image newImage) : this()
- {
+ public Capture(Image newImage) : this() {
Image = newImage;
}
///
/// Destructor
///
- ~Capture()
- {
+ ~Capture() {
Dispose(false);
}
@@ -191,8 +138,7 @@ namespace Greenshot.Base.Core
/// The public accessible Dispose
/// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
///
- public void Dispose()
- {
+ public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
@@ -202,14 +148,11 @@ namespace Greenshot.Base.Core
/// When disposing==true all non-managed resources should be freed too!
///
///
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
_image?.Dispose();
_cursor?.Dispose();
}
-
_image = null;
_cursor = null;
}
@@ -217,15 +160,13 @@ namespace Greenshot.Base.Core
///
/// Crops the capture to the specified rectangle (with Bitmap coordinates!)
///
- /// NativeRect with bitmap coordinates
- public bool Crop(NativeRect cropRectangle)
- {
+ /// Rectangle with bitmap coordinates
+ public bool Crop(Rectangle cropRectangle) {
Log.Debug("Cropping to: " + cropRectangle);
if (!ImageHelper.Crop(ref _image, ref cropRectangle))
{
return false;
}
-
_location = cropRectangle.Location;
// Change mouse location according to the cropRectangle (including screenbounds) offset
MoveMouseLocation(-cropRectangle.Location.X, -cropRectangle.Location.Y);
@@ -237,6 +178,15 @@ namespace Greenshot.Base.Core
// TODO: Remove invisible lines/words?
CaptureDetails.OcrInformation?.Offset(-cropRectangle.Location.X, -cropRectangle.Location.Y);
+ // Remove invisible elements
+ var visibleElements = new List();
+ foreach(var captureElement in _elements) {
+ if (captureElement.Bounds.IntersectsWith(cropRectangle)) {
+ visibleElements.Add(captureElement);
+ }
+ }
+ _elements = visibleElements;
+
return true;
}
@@ -246,9 +196,8 @@ namespace Greenshot.Base.Core
///
/// x coordinates to move the mouse
/// y coordinates to move the mouse
- public void MoveMouseLocation(int x, int y)
- {
- _cursorLocation = _cursorLocation.Offset(x, y);
+ public void MoveMouseLocation(int x, int y) {
+ _cursorLocation.Offset(x, y);
}
// TODO: Enable when the elements are usable again.
@@ -264,7 +213,7 @@ namespace Greenshot.Base.Core
//private void MoveElements(List listOfElements, int x, int y) {
// foreach(ICaptureElement childElement in listOfElements) {
- // NativeRect bounds = childElement.Bounds;
+ // Rectangle bounds = childElement.Bounds;
// bounds.Offset(x, y);
// childElement.Bounds = bounds;
// MoveElements(childElement.Children, x, y);
@@ -298,5 +247,6 @@ namespace Greenshot.Base.Core
// elements = value;
// }
//}
+
}
}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/CaptureDetails.cs b/GreenshotPlugin/Core/CaptureDetails.cs
new file mode 100644
index 000000000..f6e0474ea
--- /dev/null
+++ b/GreenshotPlugin/Core/CaptureDetails.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Ocr;
+
+namespace GreenshotPlugin.Core
+{
+ ///
+ /// This Class is used to pass details about the capture around.
+ /// The time the Capture was taken and the Title of the window (or a region of) that is captured
+ ///
+ public class CaptureDetails : ICaptureDetails {
+
+ ///
+ public string Title {
+ get;
+ set;
+ }
+
+ ///
+ public string Filename {
+ get;
+ set;
+ }
+
+ ///
+ public DateTime DateTime {
+ get;
+ set;
+ }
+
+ ///
+ public float DpiX {
+ get;
+ set;
+ }
+
+ ///
+ public float DpiY {
+ get;
+ set;
+ }
+
+ ///
+ public OcrInformation OcrInformation { get; set; }
+
+ ///
+ public Dictionary MetaData { get; } = new Dictionary();
+
+ ///
+ public void AddMetaData(string key, string value) {
+ if (MetaData.ContainsKey(key)) {
+ MetaData[key] = value;
+ } else {
+ MetaData.Add(key, value);
+ }
+ }
+
+ ///
+ public CaptureMode CaptureMode {
+ get;
+ set;
+ }
+
+ ///
+ public List CaptureDestinations { get; set; } = new List();
+
+ ///
+ public void ClearDestinations() {
+ CaptureDestinations.Clear();
+ }
+
+ ///
+ public void RemoveDestination(IDestination destination) {
+ if (CaptureDestinations.Contains(destination)) {
+ CaptureDestinations.Remove(destination);
+ }
+ }
+
+ ///
+ public void AddDestination(IDestination captureDestination) {
+ if (!CaptureDestinations.Contains(captureDestination)) {
+ CaptureDestinations.Add(captureDestination);
+ }
+ }
+
+ ///
+ public bool HasDestination(string designation) {
+ foreach(IDestination destination in CaptureDestinations) {
+ if (designation.Equals(destination.Designation)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public CaptureDetails() {
+ DateTime = DateTime.Now;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/CaptureElement.cs b/GreenshotPlugin/Core/CaptureElement.cs
new file mode 100644
index 000000000..6b0f3ab73
--- /dev/null
+++ b/GreenshotPlugin/Core/CaptureElement.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Drawing;
+using GreenshotPlugin.Interfaces;
+
+namespace GreenshotPlugin.Core
+{
+ ///
+ /// A class representing an element in the capture
+ ///
+ public class CaptureElement : ICaptureElement {
+ public CaptureElement(Rectangle bounds) {
+ Bounds = bounds;
+ }
+ public CaptureElement(string name) {
+ Name = name;
+ }
+ public CaptureElement(string name, Rectangle bounds) {
+ Name = name;
+ Bounds = bounds;
+ }
+
+ public List Children { get; set; } = new List();
+
+ public string Name {
+ get;
+ set;
+ }
+ public Rectangle Bounds {
+ get;
+ set;
+ }
+
+ // CaptureElements are regarded equal if their bounds are equal. this should be sufficient.
+ public override bool Equals(object obj) {
+ if (obj == null || GetType() != obj.GetType())
+ {
+ return false;
+ }
+
+ return obj is CaptureElement other && Bounds.Equals(other.Bounds);
+ }
+
+ public override int GetHashCode() {
+ // TODO: Fix this, this is not right...
+ return Bounds.GetHashCode();
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/CaptureHandler.cs b/GreenshotPlugin/Core/CaptureHandler.cs
new file mode 100644
index 000000000..4f832004a
--- /dev/null
+++ b/GreenshotPlugin/Core/CaptureHandler.cs
@@ -0,0 +1,44 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Drawing;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// This is the method signature which is used to capture a rectangle from the screen.
+ ///
+ ///
+ /// Captured Bitmap
+ public delegate Bitmap CaptureScreenRectangleHandler(Rectangle captureBounds);
+
+ ///
+ /// This is a hack to experiment with different screen capture routines
+ ///
+ public static class CaptureHandler {
+ ///
+ /// By changing this value, null is default
+ ///
+ public static CaptureScreenRectangleHandler CaptureScreenRectangle {
+ get;
+ set;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/ClipboardHelper.cs b/GreenshotPlugin/Core/ClipboardHelper.cs
new file mode 100644
index 000000000..272624c26
--- /dev/null
+++ b/GreenshotPlugin/Core/ClipboardHelper.cs
@@ -0,0 +1,847 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+using GreenshotPlugin.UnmanagedHelpers;
+using System.Runtime.InteropServices;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+using log4net;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Description of ClipboardHelper.
+ ///
+ public static class ClipboardHelper {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ClipboardHelper));
+ private static readonly object ClipboardLockObject = new object();
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ private static readonly string FORMAT_FILECONTENTS = "FileContents";
+ private static readonly string FORMAT_PNG = "PNG";
+ private static readonly string FORMAT_PNG_OFFICEART = "PNG+Office Art";
+ private static readonly string FORMAT_17 = "Format17";
+ private static readonly string FORMAT_JPG = "JPG";
+ private static readonly string FORMAT_JFIF = "JFIF";
+ private static readonly string FORMAT_JFIF_OFFICEART = "JFIF+Office Art";
+ private static readonly string FORMAT_GIF = "GIF";
+ private static readonly string FORMAT_BITMAP = "System.Drawing.Bitmap";
+ //private static readonly string FORMAT_HTML = "HTML Format";
+
+ // Template for the HTML Text on the clipboard
+ // see: http://msdn.microsoft.com/en-us/library/ms649015%28v=vs.85%29.aspx
+ // or: http://msdn.microsoft.com/en-us/library/Aa767917.aspx
+ private const string HtmlClipboardString = @"Version:0.9
+StartHTML:<<<<<<<1
+EndHTML:<<<<<<<2
+StartFragment:<<<<<<<3
+EndFragment:<<<<<<<4
+StartSelection:<<<<<<<3
+EndSelection:<<<<<<<4
+
+
+
+Greenshot capture
+
+
+
+
+
+
+";
+ private const string HtmlClipboardBase64String = @"Version:0.9
+StartHTML:<<<<<<<1
+EndHTML:<<<<<<<2
+StartFragment:<<<<<<<3
+EndFragment:<<<<<<<4
+StartSelection:<<<<<<<3
+EndSelection:<<<<<<<4
+
+
+
+Greenshot capture
+
+
+
+
+
+
+";
+
+ ///
+ /// Get the current "ClipboardOwner" but only if it isn't us!
+ ///
+ /// current clipboard owner
+ private static string GetClipboardOwner() {
+ string owner = null;
+ try {
+ IntPtr hWnd = User32.GetClipboardOwner();
+ if (hWnd != IntPtr.Zero) {
+ try
+ {
+ User32.GetWindowThreadProcessId(hWnd, out var pid);
+ using Process me = Process.GetCurrentProcess();
+ using Process ownerProcess = Process.GetProcessById(pid);
+ // Exclude myself
+ if (me.Id != ownerProcess.Id)
+ {
+ // Get Process Name
+ owner = ownerProcess.ProcessName;
+ // Try to get the starting Process Filename, this might fail.
+ try
+ {
+ owner = ownerProcess.Modules[0].FileName;
+ }
+ catch (Exception)
+ {
+ // Ignore
+ }
+ }
+ }
+ catch(Exception e)
+ {
+ Log.Warn("Non critical error: Couldn't get clipboard process, trying to use the title.", e);
+ var title = new StringBuilder(260, 260);
+ User32.GetWindowText(hWnd, title, title.Capacity);
+ owner = title.ToString();
+ }
+ }
+ } catch (Exception e) {
+ Log.Warn("Non critical error: Couldn't get clipboard owner.", e);
+ }
+ return owner;
+ }
+
+ ///
+ /// The SetDataObject will lock/try/catch clipboard operations making it save and not show exceptions.
+ /// The bool "copy" is used to decided if the information stays on the clipboard after exit.
+ ///
+ ///
+ ///
+ private static void SetDataObject(IDataObject ido, bool copy) {
+ lock (ClipboardLockObject) {
+ // Clear first, this seems to solve some issues
+ try
+ {
+ Clipboard.Clear();
+ }
+ catch (Exception clearException)
+ {
+ Log.Warn(clearException.Message);
+ }
+ try
+ {
+ // For BUG-1935 this was changed from looping ourselfs, or letting MS retry...
+ Clipboard.SetDataObject(ido, copy, 15, 200);
+ }
+ catch (Exception clipboardSetException)
+ {
+ string messageText;
+ string clipboardOwner = GetClipboardOwner();
+ if (clipboardOwner != null)
+ {
+ messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner);
+ }
+ else {
+ messageText = Language.GetString("clipboard_error");
+ }
+ Log.Error(messageText, clipboardSetException);
+ }
+ }
+ }
+
+ ///
+ /// The GetDataObject will lock/try/catch clipboard operations making it save and not show exceptions.
+ ///
+ public static IDataObject GetDataObject() {
+ lock (ClipboardLockObject) {
+ int retryCount = 2;
+ while (retryCount >= 0) {
+ try {
+ return Clipboard.GetDataObject();
+ } catch (Exception ee) {
+ if (retryCount == 0) {
+ string messageText;
+ string clipboardOwner = GetClipboardOwner();
+ if (clipboardOwner != null) {
+ messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner);
+ } else {
+ messageText = Language.GetString("clipboard_error");
+ }
+ Log.Error(messageText, ee);
+ } else {
+ Thread.Sleep(100);
+ }
+ } finally {
+ --retryCount;
+ }
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Wrapper for Clipboard.ContainsText, Created for Bug #3432313
+ ///
+ /// boolean if there is text on the clipboard
+ public static bool ContainsText() {
+ IDataObject clipboardData = GetDataObject();
+ return ContainsText(clipboardData);
+ }
+
+ ///
+ /// Test if the IDataObject contains Text
+ ///
+ ///
+ ///
+ public static bool ContainsText(IDataObject dataObject) {
+ if (dataObject != null) {
+ if (dataObject.GetDataPresent(DataFormats.Text) || dataObject.GetDataPresent(DataFormats.UnicodeText)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Wrapper for Clipboard.ContainsImage, specialized for Greenshot, Created for Bug #3432313
+ ///
+ /// boolean if there is an image on the clipboard
+ public static bool ContainsImage() {
+ IDataObject clipboardData = GetDataObject();
+ return ContainsImage(clipboardData);
+ }
+
+ ///
+ /// Check if the IDataObject has an image
+ ///
+ ///
+ /// true if an image is there
+ public static bool ContainsImage(IDataObject dataObject) {
+ if (dataObject != null) {
+ if (dataObject.GetDataPresent(DataFormats.Bitmap)
+ || dataObject.GetDataPresent(DataFormats.Dib)
+ || dataObject.GetDataPresent(DataFormats.Tiff)
+ || dataObject.GetDataPresent(DataFormats.EnhancedMetafile)
+ || dataObject.GetDataPresent(FORMAT_PNG)
+ || dataObject.GetDataPresent(FORMAT_17)
+ || dataObject.GetDataPresent(FORMAT_JPG)
+ || dataObject.GetDataPresent(FORMAT_GIF)) {
+ return true;
+ }
+ var imageFiles = GetImageFilenames(dataObject);
+ if (imageFiles.Any()) {
+ return true;
+ }
+ if (dataObject.GetDataPresent(FORMAT_FILECONTENTS)) {
+ try {
+ MemoryStream imageStream = dataObject.GetData(FORMAT_FILECONTENTS) as MemoryStream;
+ if (IsValidStream(imageStream)) {
+ using (ImageHelper.FromStream(imageStream))
+ {
+ // If we get here, there is an image
+ return true;
+ }
+ }
+ } catch (Exception) {
+ // Ignore
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Simple helper to check the stream
+ ///
+ ///
+ ///
+ private static bool IsValidStream(MemoryStream memoryStream) {
+ return memoryStream != null && memoryStream.Length > 0;
+ }
+
+ ///
+ /// Wrapper for Clipboard.GetImage, Created for Bug #3432313
+ ///
+ /// Image if there is an image on the clipboard
+ public static Image GetImage() {
+ IDataObject clipboardData = GetDataObject();
+ // Return the first image
+ foreach (Image clipboardImage in GetImages(clipboardData)) {
+ return clipboardImage;
+ }
+ return null;
+ }
+
+ ///
+ /// Get all images (multiple if filenames are available) from the dataObject
+ /// Returned images must be disposed by the calling code!
+ ///
+ ///
+ /// IEnumerable of Image
+ public static IEnumerable GetImages(IDataObject dataObject) {
+ // Get single image, this takes the "best" match
+ Image singleImage = GetImage(dataObject);
+ if (singleImage != null) {
+ Log.InfoFormat("Got image from clipboard with size {0} and format {1}", singleImage.Size, singleImage.PixelFormat);
+ yield return singleImage;
+ } else {
+ // check if files are supplied
+ foreach (string imageFile in GetImageFilenames(dataObject)) {
+ Image returnImage = null;
+ try {
+ returnImage = ImageHelper.LoadImage(imageFile);
+ } catch (Exception streamImageEx) {
+ Log.Error("Problem retrieving Image from clipboard.", streamImageEx);
+ }
+ if (returnImage != null) {
+ Log.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat);
+ yield return returnImage;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Get an Image from the IDataObject, don't check for FileDrop
+ ///
+ ///
+ /// Image or null
+ private static Image GetImage(IDataObject dataObject) {
+ Image returnImage = null;
+ if (dataObject != null) {
+ IList formats = GetFormats(dataObject);
+ string[] retrieveFormats;
+
+ // Found a weird bug, where PNG's from Outlook 2010 are clipped
+ // So I build some special logik to get the best format:
+ if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) {
+ // Outlook ??
+ Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front...");
+ retrieveFormats = new[] { DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JFIF, DataFormats.Tiff, FORMAT_GIF };
+ } else {
+ retrieveFormats = new[] { FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_GIF };
+ }
+ foreach (string currentFormat in retrieveFormats) {
+ if (formats != null && formats.Contains(currentFormat)) {
+ Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
+ returnImage = GetImageForFormat(currentFormat, dataObject);
+ } else {
+ Log.DebugFormat("Couldn't find format {0}.", currentFormat);
+ }
+ if (returnImage != null) {
+ return returnImage;
+ }
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Helper method to try to get an image in the specified format from the dataObject
+ /// the DIB reader should solve some issues
+ /// It also supports Format17/DibV5, by using the following information: http://stackoverflow.com/a/14335591
+ ///
+ /// string with the format
+ /// IDataObject
+ /// Image or null
+ private static Image GetImageForFormat(string format, IDataObject dataObject) {
+ object clipboardObject = GetFromDataObject(dataObject, format);
+ MemoryStream imageStream = clipboardObject as MemoryStream;
+ if (!IsValidStream(imageStream)) {
+ // TODO: add "HTML Format" support here...
+ return clipboardObject as Image;
+ }
+ if (CoreConfig.EnableSpecialDIBClipboardReader) {
+ if (format == FORMAT_17 || format == DataFormats.Dib) {
+ Log.Info("Found DIB stream, trying to process it.");
+ try {
+ if (imageStream != null)
+ {
+ byte[] dibBuffer = new byte[imageStream.Length];
+ imageStream.Read(dibBuffer, 0, dibBuffer.Length);
+ BITMAPINFOHEADER infoHeader = BinaryStructHelper.FromByteArray(dibBuffer);
+ if (!infoHeader.IsDibV5) {
+ Log.InfoFormat("Using special DIB
+ /// Wrapper for Clipboard.GetText created for Bug #3432313
+ ///
+ /// string if there is text on the clipboard
+ public static string GetText() {
+ return GetText(GetDataObject());
+ }
+
+ ///
+ /// Get Text from the DataObject
+ ///
+ /// string if there is text on the clipboard
+ public static string GetText(IDataObject dataObject) {
+ if (ContainsText(dataObject)) {
+ return (string)dataObject.GetData(DataFormats.Text);
+ }
+ return null;
+ }
+
+ ///
+ /// Set text to the clipboard
+ ///
+ ///
+ public static void SetClipboardData(string text) {
+ IDataObject ido = new DataObject();
+ ido.SetData(DataFormats.Text, true, text);
+ SetDataObject(ido, true);
+ }
+
+ private static string GetHtmlString(ISurface surface, string filename) {
+ string utf8EncodedHtmlString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HtmlClipboardString));
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${width}", surface.Image.Width.ToString());
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${height}", surface.Image.Height.ToString());
+ utf8EncodedHtmlString= utf8EncodedHtmlString.Replace("${file}", filename.Replace("\\","/"));
+ StringBuilder sb=new StringBuilder();
+ sb.Append(utf8EncodedHtmlString);
+ sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
+ sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
+ sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)+"".Length).ToString("D8"));
+ sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
+ return sb.ToString();
+ }
+
+ private static string GetHtmlDataUrlString(ISurface surface, MemoryStream pngStream) {
+ string utf8EncodedHtmlString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HtmlClipboardBase64String));
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${width}", surface.Image.Width.ToString());
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${height}", surface.Image.Height.ToString());
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${format}", "png");
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${data}", Convert.ToBase64String(pngStream.GetBuffer(),0, (int)pngStream.Length));
+ StringBuilder sb=new StringBuilder();
+ sb.Append(utf8EncodedHtmlString);
+ sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
+ sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
+ sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)+"".Length).ToString("D8"));
+ sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
+ return sb.ToString();
+ }
+
+ private const int BITMAPFILEHEADER_LENGTH = 14;
+ ///
+ /// Set an Image to the clipboard
+ /// This method will place images to the clipboard depending on the ClipboardFormats setting.
+ /// e.g. Bitmap which works with pretty much everything and type Dib for e.g. OpenOffice
+ /// because OpenOffice has a bug http://qa.openoffice.org/issues/show_bug.cgi?id=85661
+ /// The Dib (Device Indenpendend Bitmap) in 32bpp actually won't work with Powerpoint 2003!
+ /// When pasting a Dib in PP 2003 the Bitmap is somehow shifted left!
+ /// For this problem the user should not use the direct paste (=Dib), but select Bitmap
+ ///
+ public static void SetClipboardData(ISurface surface) {
+ DataObject dataObject = new DataObject();
+
+ // This will work for Office and most other applications
+ //ido.SetData(DataFormats.Bitmap, true, image);
+
+ MemoryStream dibStream = null;
+ MemoryStream dibV5Stream = null;
+ MemoryStream pngStream = null;
+ Image imageToSave = null;
+ bool disposeImage = false;
+ try {
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
+ // Create the image which is going to be saved so we don't create it multiple times
+ disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave);
+ try {
+ // Create PNG stream
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.PNG)) {
+ pngStream = new MemoryStream();
+ // PNG works for e.g. Powerpoint
+ SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
+ ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings);
+ pngStream.Seek(0, SeekOrigin.Begin);
+ // Set the PNG stream
+ dataObject.SetData(FORMAT_PNG, false, pngStream);
+ }
+ } catch (Exception pngEx) {
+ Log.Error("Error creating PNG for the Clipboard.", pngEx);
+ }
+
+ try {
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIB)) {
+ using (MemoryStream tmpBmpStream = new MemoryStream()) {
+ // Save image as BMP
+ SurfaceOutputSettings bmpOutputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 100, false);
+ ImageOutput.SaveToStream(imageToSave, null, tmpBmpStream, bmpOutputSettings);
+
+ dibStream = new MemoryStream();
+ // Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14
+ dibStream.Write(tmpBmpStream.GetBuffer(), BITMAPFILEHEADER_LENGTH, (int)tmpBmpStream.Length - BITMAPFILEHEADER_LENGTH);
+ }
+
+ // Set the DIB to the clipboard DataObject
+ dataObject.SetData(DataFormats.Dib, true, dibStream);
+ }
+ } catch (Exception dibEx) {
+ Log.Error("Error creating DIB for the Clipboard.", dibEx);
+ }
+
+ // CF_DibV5
+ try {
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIBV5)) {
+ // Create the stream for the clipboard
+ dibV5Stream = new MemoryStream();
+
+ // Create the BITMAPINFOHEADER
+ BITMAPINFOHEADER header = new BITMAPINFOHEADER(imageToSave.Width, imageToSave.Height, 32)
+ {
+ // Make sure we have BI_BITFIELDS, this seems to be normal for Format17?
+ biCompression = BI_COMPRESSION.BI_BITFIELDS
+ };
+ // Create a byte[] to write
+ byte[] headerBytes = BinaryStructHelper.ToByteArray(header);
+ // Write the BITMAPINFOHEADER to the stream
+ dibV5Stream.Write(headerBytes, 0, headerBytes.Length);
+
+ // As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added
+ BitfieldColorMask colorMask = new BitfieldColorMask();
+ // Make sure the values are set
+ colorMask.InitValues();
+ // Create the byte[] from the struct
+ byte[] colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask);
+ Array.Reverse(colorMaskBytes);
+ // Write to the stream
+ dibV5Stream.Write(colorMaskBytes, 0, colorMaskBytes.Length);
+
+ // Create the raw bytes for the pixels only
+ byte[] bitmapBytes = BitmapToByteArray((Bitmap)imageToSave);
+ // Write to the stream
+ dibV5Stream.Write(bitmapBytes, 0, bitmapBytes.Length);
+
+ // Set the DIBv5 to the clipboard DataObject
+ dataObject.SetData(FORMAT_17, true, dibV5Stream);
+ }
+ } catch (Exception dibEx) {
+ Log.Error("Error creating DIB for the Clipboard.", dibEx);
+ }
+
+ // Set the HTML
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML)) {
+ string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null);
+ string html = GetHtmlString(surface, tmpFile);
+ dataObject.SetText(html, TextDataFormat.Html);
+ } else if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTMLDATAURL)) {
+ string html;
+ using (MemoryStream tmpPngStream = new MemoryStream()) {
+ SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false)
+ {
+ // Do not allow to reduce the colors, some applications dislike 256 color images
+ // reported with bug #3594681
+ DisableReduceColors = true
+ };
+ // Check if we can use the previously used image
+ if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed) {
+ ImageOutput.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings);
+ } else {
+ ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings);
+ }
+ html = GetHtmlDataUrlString(surface, tmpPngStream);
+ }
+ dataObject.SetText(html, TextDataFormat.Html);
+ }
+ } finally {
+ // we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone!
+ // Check if Bitmap is wanted
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.BITMAP)) {
+ dataObject.SetImage(imageToSave);
+ // Place the DataObject to the clipboard
+ SetDataObject(dataObject, true);
+ } else {
+ // Place the DataObject to the clipboard
+ SetDataObject(dataObject, true);
+ }
+
+ pngStream?.Dispose();
+ dibStream?.Dispose();
+ dibV5Stream?.Dispose();
+ // cleanup if needed
+ if (disposeImage) {
+ imageToSave?.Dispose();
+ }
+ }
+ }
+
+ ///
+ /// Helper method so get the bitmap bytes
+ /// See: http://stackoverflow.com/a/6570155
+ ///
+ /// Bitmap
+ /// byte[]
+ private static byte[] BitmapToByteArray(Bitmap bitmap) {
+ // Lock the bitmap's bits.
+ Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+ BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
+
+ int absStride = Math.Abs(bmpData.Stride);
+ int bytes = absStride * bitmap.Height;
+ long ptr = bmpData.Scan0.ToInt32();
+ // Declare an array to hold the bytes of the bitmap.
+ byte[] rgbValues = new byte[bytes];
+
+ for (int i = 0; i < bitmap.Height; i++) {
+ IntPtr pointer = new IntPtr(ptr + (bmpData.Stride * i));
+ Marshal.Copy(pointer, rgbValues, absStride * (bitmap.Height - i - 1), absStride);
+ }
+
+ // Unlock the bits.
+ bitmap.UnlockBits(bmpData);
+
+ return rgbValues;
+ }
+
+ ///
+ /// Set Object with type Type to the clipboard
+ ///
+ /// Type
+ /// object
+ public static void SetClipboardData(Type type, object obj) {
+ DataFormats.Format format = DataFormats.GetFormat(type.FullName);
+
+ //now copy to clipboard
+ IDataObject dataObj = new DataObject();
+ dataObj.SetData(format.Name, false, obj);
+ // Use false to make the object dissapear when the application stops.
+ SetDataObject(dataObj, true);
+ }
+
+ ///
+ /// Retrieve a list of all formats currently on the clipboard
+ ///
+ /// List of strings with the current formats
+ public static List GetFormats() {
+ return GetFormats(GetDataObject());
+ }
+
+ ///
+ /// Retrieve a list of all formats currently in the IDataObject
+ ///
+ /// List of string with the current formats
+ public static List GetFormats(IDataObject dataObj) {
+ string[] formats = null;
+
+ if (dataObj != null) {
+ formats = dataObj.GetFormats();
+ }
+ if (formats != null) {
+ Log.DebugFormat("Got clipboard formats: {0}", string.Join(",", formats));
+ return new List(formats);
+ }
+ return new List();
+ }
+
+ ///
+ /// Check if there is currently something in the dataObject which has the supplied format
+ ///
+ /// string with format
+ /// true if one the format is found
+ public static bool ContainsFormat(string format) {
+ return ContainsFormat(GetDataObject(), new[]{format});
+ }
+
+ ///
+ /// Check if there is currently something on the clipboard which has the supplied format
+ ///
+ /// IDataObject
+ /// string with format
+ /// true if one the format is found
+ public static bool ContainsFormat(IDataObject dataObject, string format) {
+ return ContainsFormat(dataObject, new[] { format });
+ }
+
+ ///
+ /// Check if there is currently something on the clipboard which has one of the supplied formats
+ ///
+ /// string[] with formats
+ /// true if one of the formats was found
+ public static bool ContainsFormat(string[] formats) {
+ return ContainsFormat(GetDataObject(), formats);
+ }
+
+ ///
+ /// Check if there is currently something on the clipboard which has one of the supplied formats
+ ///
+ /// IDataObject
+ /// string[] with formats
+ /// true if one of the formats was found
+ public static bool ContainsFormat(IDataObject dataObject, string[] formats) {
+ bool formatFound = false;
+ List currentFormats = GetFormats(dataObject);
+ if (currentFormats == null || currentFormats.Count == 0 || formats == null || formats.Length == 0) {
+ return false;
+ }
+ foreach (string format in formats) {
+ if (currentFormats.Contains(format)) {
+ formatFound = true;
+ break;
+ }
+ }
+ return formatFound;
+ }
+
+ ///
+ /// Get Object of type Type from the clipboard
+ ///
+ /// Type to get
+ /// object from clipboard
+ public static object GetClipboardData(Type type) {
+ string format = type.FullName;
+ return GetClipboardData(format);
+ }
+
+ ///
+ /// Get Object for format from IDataObject
+ ///
+ /// IDataObject
+ /// Type to get
+ /// object from IDataObject
+ public static object GetFromDataObject(IDataObject dataObj, Type type) {
+ if (type != null) {
+ return GetFromDataObject(dataObj, type.FullName);
+ }
+ return null;
+ }
+
+ ///
+ /// Get ImageFilenames from the IDataObject
+ ///
+ /// IDataObject
+ ///
+ public static IEnumerable GetImageFilenames(IDataObject dataObject) {
+ string[] dropFileNames = (string[]) dataObject.GetData(DataFormats.FileDrop);
+ if (dropFileNames != null && dropFileNames.Length > 0)
+ {
+ return dropFileNames
+ .Where(filename => !string.IsNullOrEmpty(filename))
+ .Where(Path.HasExtension)
+ .Where(filename => ImageHelper.StreamConverters.Keys.Contains(Path.GetExtension(filename).ToLowerInvariant().Substring(1)));
+ }
+ return Enumerable.Empty();
+ }
+
+ ///
+ /// Get Object for format from IDataObject
+ ///
+ /// IDataObject
+ /// format to get
+ /// object from IDataObject
+ public static object GetFromDataObject(IDataObject dataObj, string format) {
+ if (dataObj != null) {
+ try {
+ return dataObj.GetData(format);
+ } catch (Exception e) {
+ Log.Error("Error in GetClipboardData.", e);
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Get Object for format from the clipboard
+ ///
+ /// format to get
+ /// object from clipboard
+ public static object GetClipboardData(string format) {
+ return GetFromDataObject(GetDataObject(), format);
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/CoreConfiguration.cs b/GreenshotPlugin/Core/CoreConfiguration.cs
new file mode 100644
index 000000000..1db95dd93
--- /dev/null
+++ b/GreenshotPlugin/Core/CoreConfiguration.cs
@@ -0,0 +1,545 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.IO;
+using System.Reflection;
+using System.Windows.Forms;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+
+namespace GreenshotPlugin.Core {
+ public enum ClipboardFormat {
+ PNG, DIB, HTML, HTMLDATAURL, BITMAP, DIBV5
+ }
+ public enum OutputFormat {
+ bmp, gif, jpg, png, tiff, greenshot, ico
+ }
+ public enum WindowCaptureMode {
+ Screen, GDI, Aero, AeroTransparent, Auto
+ }
+
+ public enum BuildStates {
+ UNSTABLE,
+ RELEASE_CANDIDATE,
+ RELEASE
+ }
+
+ public enum ClickActions {
+ DO_NOTHING,
+ OPEN_LAST_IN_EXPLORER,
+ OPEN_LAST_IN_EDITOR,
+ OPEN_SETTINGS,
+ SHOW_CONTEXT_MENU
+ }
+
+ ///
+ /// Description of CoreConfiguration.
+ ///
+ [IniSection("Core", Description="Greenshot core configuration")]
+ public class CoreConfiguration : IniSection, INotifyPropertyChanged {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [IniProperty("Language", Description = "The language in IETF format (e.g. en-US)")]
+ public string Language { get; set; }
+
+ [IniProperty("RegionHotkey", Description="Hotkey for starting the region capture", DefaultValue="PrintScreen")]
+ public string RegionHotkey { get; set; }
+ [IniProperty("WindowHotkey", Description="Hotkey for starting the window capture", DefaultValue="Alt + PrintScreen")]
+ public string WindowHotkey { get; set; }
+ [IniProperty("FullscreenHotkey", Description="Hotkey for starting the fullscreen capture", DefaultValue="Ctrl + PrintScreen")]
+ public string FullscreenHotkey { get; set; }
+ [IniProperty("LastregionHotkey", Description="Hotkey for starting the last region capture", DefaultValue="Shift + PrintScreen")]
+ public string LastregionHotkey { get; set; }
+ [IniProperty("IEHotkey", Description="Hotkey for starting the IE capture", DefaultValue="Shift + Ctrl + PrintScreen")]
+ public string IEHotkey { get; set; }
+
+ [IniProperty("IsFirstLaunch", Description="Is this the first time launch?", DefaultValue="true")]
+ public bool IsFirstLaunch { get; set; }
+ [IniProperty("Destinations", Separator=",", Description="Which destinations? Possible options (more might be added by plugins) are: Editor, FileDefault, FileWithDialog, Clipboard, Printer, EMail, Picker", DefaultValue="Picker")]
+ public List OutputDestinations { get; set; } = new List();
+ [IniProperty("ClipboardFormats", Separator=",", Description="Specify which formats we copy on the clipboard? Options are: PNG, HTML, HTMLDATAURL and DIB", DefaultValue="PNG,DIB")]
+ public List ClipboardFormats { get; set; } = new List();
+
+ [IniProperty("CaptureMousepointer", Description="Should the mouse be captured?", DefaultValue="true")]
+ public bool CaptureMousepointer { get; set; }
+ [IniProperty("CaptureWindowsInteractive", Description="Use interactive window selection to capture? (false=Capture active window)", DefaultValue="false")]
+ public bool CaptureWindowsInteractive { get; set; }
+ [IniProperty("CaptureDelay", Description="Capture delay in millseconds.", DefaultValue="100")]
+ public int CaptureDelay { get; set; }
+ [IniProperty("ScreenCaptureMode", Description = "The capture mode used to capture a screen. (Auto, FullScreen, Fixed)", DefaultValue = "Auto")]
+ public ScreenCaptureMode ScreenCaptureMode { get; set; }
+ [IniProperty("ScreenToCapture", Description = "The screen number to capture when using ScreenCaptureMode Fixed.", DefaultValue = "1")]
+ public int ScreenToCapture { get; set; }
+ [IniProperty("WindowCaptureMode", Description = "The capture mode used to capture a Window (Screen, GDI, Aero, AeroTransparent, Auto).", DefaultValue = "Auto")]
+ public WindowCaptureMode WindowCaptureMode { get; set; }
+ [IniProperty("WindowCaptureAllChildLocations", Description="Enable/disable capture all children, very slow but will make it possible to use this information in the editor.", DefaultValue="False")]
+ public bool WindowCaptureAllChildLocations { get; set; }
+
+ [IniProperty("DWMBackgroundColor", Description="The background color for a DWM window capture.")]
+ public Color DWMBackgroundColor { get; set; }
+
+ [IniProperty("PlayCameraSound", LanguageKey="settings_playsound",Description="Play a camera sound after taking a capture.", DefaultValue="false")]
+ public bool PlayCameraSound { get; set; } = false;
+ [IniProperty("ShowTrayNotification", LanguageKey="settings_shownotify",Description="Show a notification from the systray when a capture is taken.", DefaultValue="true")]
+ public bool ShowTrayNotification { get; set; } = true;
+
+ [IniProperty("OutputFilePath", Description="Output file path.")]
+ public string OutputFilePath { get; set; }
+ [IniProperty("OutputFileAllowOverwrite", Description = "If the target file already exists True will make Greenshot always overwrite and False will display a 'Save-As' dialog.", DefaultValue = "true")]
+ public bool OutputFileAllowOverwrite { get; set; }
+ [IniProperty("OutputFileFilenamePattern", Description = "Filename pattern for screenshot.", DefaultValue = "${capturetime:d\"yyyy-MM-dd HH_mm_ss\"}-${title}")]
+ public string OutputFileFilenamePattern { get; set; }
+ [IniProperty("OutputFileFormat", Description="Default file type for writing screenshots. (bmp, gif, jpg, png, tiff)", DefaultValue="png")]
+ public OutputFormat OutputFileFormat { get; set; } = OutputFormat.png;
+ [IniProperty("OutputFileReduceColors", Description="If set to true, than the colors of the output file are reduced to 256 (8-bit) colors", DefaultValue="false")]
+ public bool OutputFileReduceColors { get; set; }
+ [IniProperty("OutputFileAutoReduceColors", Description = "If set to true the amount of colors is counted and if smaller than 256 the color reduction is automatically used.", DefaultValue = "false")]
+ public bool OutputFileAutoReduceColors { get; set; }
+ [IniProperty("OutputFileReduceColorsTo", Description = "Amount of colors to reduce to, when reducing", DefaultValue = "256")]
+ public int OutputFileReduceColorsTo { get; set; }
+
+ [IniProperty("OutputFileCopyPathToClipboard", Description="When saving a screenshot, copy the path to the clipboard?", DefaultValue="true")]
+ public bool OutputFileCopyPathToClipboard { get; set; }
+ [IniProperty("OutputFileAsFullpath", Description="SaveAs Full path?")]
+ public string OutputFileAsFullpath { get; set; }
+
+ [IniProperty("OutputFileJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
+ public int OutputFileJpegQuality { get; set; }
+ [IniProperty("OutputFilePromptQuality", Description="Ask for the quality before saving?", DefaultValue="false")]
+ public bool OutputFilePromptQuality { get; set; }
+ [IniProperty("OutputFileIncrementingNumber", Description="The number for the ${NUM} in the filename pattern, is increased automatically after each save.", DefaultValue="1")]
+ public uint OutputFileIncrementingNumber { get; set; }
+
+ [IniProperty("OutputPrintPromptOptions", LanguageKey="settings_alwaysshowprintoptionsdialog", Description="Ask for print options when printing?", DefaultValue="true")]
+ public bool OutputPrintPromptOptions { get; set; }
+ [IniProperty("OutputPrintAllowRotate", LanguageKey="printoptions_allowrotate", Description="Allow rotating the picture for fitting on paper?", DefaultValue="false")]
+ public bool OutputPrintAllowRotate { get; set; }
+ [IniProperty("OutputPrintAllowEnlarge", LanguageKey="printoptions_allowenlarge", Description="Allow growing the picture for fitting on paper?", DefaultValue="false")]
+ public bool OutputPrintAllowEnlarge { get; set; }
+ [IniProperty("OutputPrintAllowShrink", LanguageKey="printoptions_allowshrink", Description="Allow shrinking the picture for fitting on paper?", DefaultValue="true")]
+ public bool OutputPrintAllowShrink { get; set; }
+ [IniProperty("OutputPrintCenter", LanguageKey="printoptions_allowcenter", Description="Center image when printing?", DefaultValue="true")]
+ public bool OutputPrintCenter { get; set; }
+ [IniProperty("OutputPrintInverted", LanguageKey="printoptions_inverted", Description="Print image inverted (use e.g. for console captures)", DefaultValue="false")]
+ public bool OutputPrintInverted { get; set; }
+ [IniProperty("OutputPrintGrayscale", LanguageKey = "printoptions_printgrayscale", Description = "Force grayscale printing", DefaultValue = "false")]
+ public bool OutputPrintGrayscale { get; set; }
+ [IniProperty("OutputPrintMonochrome", LanguageKey = "printoptions_printmonochrome", Description = "Force monorchrome printing", DefaultValue = "false")]
+ public bool OutputPrintMonochrome { get; set; }
+ [IniProperty("OutputPrintMonochromeThreshold", Description = "Threshold for monochrome filter (0 - 255), lower value means less black", DefaultValue = "127")]
+ public byte OutputPrintMonochromeThreshold { get; set; }
+ [IniProperty("OutputPrintFooter", LanguageKey = "printoptions_timestamp", Description = "Print footer on print?", DefaultValue = "true")]
+ public bool OutputPrintFooter { get; set; }
+ [IniProperty("OutputPrintFooterPattern", Description = "Footer pattern", DefaultValue = "${capturetime:d\"D\"} ${capturetime:d\"T\"} - ${title}")]
+ public string OutputPrintFooterPattern { get; set; }
+ [IniProperty("NotificationSound", Description = "The wav-file to play when a capture is taken, loaded only once at the Greenshot startup", DefaultValue="default")]
+ public string NotificationSound { get; set; }
+ [IniProperty("UseProxy", Description="Use your global proxy?", DefaultValue="True")]
+ public bool UseProxy { get; set; }
+ [IniProperty("IECapture", Description="Enable/disable IE capture", DefaultValue="True")]
+ public bool IECapture { get; set; }
+ [IniProperty("IEFieldCapture", Description="Enable/disable IE field capture, very slow but will make it possible to annotate the fields of a capture in the editor.", DefaultValue="False")]
+ public bool IEFieldCapture { get; set; }
+ [IniProperty("WindowClassesToCheckForIE", Description = "Comma separated list of Window-Classes which need to be checked for a IE instance!", DefaultValue = "AfxFrameOrView70,IMWindowClass")]
+ public List WindowClassesToCheckForIE { get; set; }
+ [IniProperty("AutoCropDifference", Description="Sets how to compare the colors for the autocrop detection, the higher the more is 'selected'. Possible values are from 0 to 255, where everything above ~150 doesn't make much sense!", DefaultValue="10")]
+ public int AutoCropDifference { get; set; }
+
+ [IniProperty("IncludePlugins", Description="Comma separated list of Plugins which are allowed. If something in the list, than every plugin not in the list will not be loaded!")]
+ public List IncludePlugins { get; set; }
+ [IniProperty("ExcludePlugins", Description="Comma separated list of Plugins which are NOT allowed.")]
+ public List ExcludePlugins { get; set; }
+ [IniProperty("ExcludeDestinations", Description = "Comma separated list of destinations which should be disabled.")]
+ public List ExcludeDestinations { get; set; }
+
+ [IniProperty("UpdateCheckInterval", Description="How many days between every update check? (0=no checks)", DefaultValue="14")]
+ public int UpdateCheckInterval { get; set; }
+ [IniProperty("LastUpdateCheck", Description="Last update check")]
+ public DateTime LastUpdateCheck { get; set; }
+
+ [IniProperty("DisableSettings", Description = "Enable/disable the access to the settings, can only be changed manually in this .ini", DefaultValue = "False")]
+ public bool DisableSettings { get; set; }
+ [IniProperty("DisableQuickSettings", Description = "Enable/disable the access to the quick settings, can only be changed manually in this .ini", DefaultValue = "False")]
+ public bool DisableQuickSettings { get; set; }
+ [IniProperty("DisableTrayicon", Description = "Disable the trayicon, can only be changed manually in this .ini", DefaultValue = "False")]
+ public bool HideTrayicon { get; set; }
+ [IniProperty("HideExpertSettings", Description = "Hide expert tab in the settings, can only be changed manually in this .ini", DefaultValue = "False")]
+ public bool HideExpertSettings { get; set; }
+
+ [IniProperty("ThumnailPreview", Description="Enable/disable thumbnail previews", DefaultValue="True")]
+ public bool ThumnailPreview { get; set; }
+
+ [IniProperty("NoGDICaptureForProduct", Description = "List of productnames for which GDI capturing is skipped (using fallback).", DefaultValue = "IntelliJ IDEA")]
+ public List NoGDICaptureForProduct { get; set; }
+ [IniProperty("NoDWMCaptureForProduct", Description = "List of productnames for which DWM capturing is skipped (using fallback).", DefaultValue = "Citrix ICA Client")]
+ public List NoDWMCaptureForProduct { get; set; }
+
+ [IniProperty("OptimizeForRDP", Description="Make some optimizations for usage with remote desktop", DefaultValue="False")]
+ public bool OptimizeForRDP { get; set; }
+ [IniProperty("DisableRDPOptimizing", Description = "Disable all optimizations for usage with remote desktop", DefaultValue = "False")]
+ public bool DisableRDPOptimizing { get; set; }
+
+ [IniProperty("MinimizeWorkingSetSize", Description="Optimize memory footprint, but with a performance penalty!", DefaultValue="False")]
+ public bool MinimizeWorkingSetSize { get; set; }
+
+ [IniProperty("WindowCaptureRemoveCorners", Description = "Remove the corners from a window capture", DefaultValue = "True")]
+ public bool WindowCaptureRemoveCorners { get; set; }
+
+ [IniProperty("CheckForUnstable", Description = "Also check for unstable version updates", DefaultValue = "False")]
+ public bool CheckForUnstable { get; set; }
+
+ [IniProperty("ActiveTitleFixes", Description="The fixes that are active.")]
+ public List ActiveTitleFixes { get; set; }
+
+ [IniProperty("TitleFixMatcher", Description="The regular expressions to match the title with.")]
+ public Dictionary TitleFixMatcher { get; set; }
+
+ [IniProperty("TitleFixReplacer", Description="The replacements for the matchers.")]
+ public Dictionary TitleFixReplacer { get; set; }
+
+ [IniProperty("ExperimentalFeatures", Description="A list of experimental features, this allows us to test certain features before releasing them.", ExcludeIfNull=true)]
+ public List ExperimentalFeatures { get; set; }
+
+ [IniProperty("EnableSpecialDIBClipboardReader", Description = "Enable a special DIB clipboard reader", DefaultValue="True")]
+ public bool EnableSpecialDIBClipboardReader { get; set; }
+
+ [IniProperty("WindowCornerCutShape", Description = "The cutshape which is used to remove the window corners, is mirrorred for all corners", DefaultValue = "5,3,2,1,1")]
+ public List WindowCornerCutShape { get; set; }
+
+ [IniProperty("LeftClickAction", Description = "Specify what action is made if the tray icon is left clicked, if a double-click action is specified this action is initiated after a delay (configurable via the windows double-click speed)", DefaultValue = "SHOW_CONTEXT_MENU")]
+ public ClickActions LeftClickAction { get; set; }
+
+ [IniProperty("DoubleClickAction", Description = "Specify what action is made if the tray icon is double clicked", DefaultValue = "OPEN_LAST_IN_EXPLORER")]
+ public ClickActions DoubleClickAction { get; set; }
+
+ [IniProperty("ZoomerEnabled", Description = "Sets if the zoomer is enabled", DefaultValue = "True")]
+ public bool ZoomerEnabled { get; set; }
+ [IniProperty("ZoomerOpacity", Description = "Specify the transparency for the zoomer, from 0-1 (where 1 is no transparency and 0 is complete transparent. An usefull setting would be 0.7)", DefaultValue = "1")]
+ public float ZoomerOpacity { get; set; }
+
+ [IniProperty("MaxMenuItemLength", Description = "Maximum length of submenu items in the context menu, making this longer might cause context menu issues on dual screen systems.", DefaultValue = "25")]
+ public int MaxMenuItemLength { get; set; }
+
+ [IniProperty("MailApiTo", Description = "The 'to' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
+ public string MailApiTo { get; set; }
+ [IniProperty("MailApiCC", Description = "The 'CC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
+ public string MailApiCC { get; set; }
+ [IniProperty("MailApiBCC", Description = "The 'BCC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
+ public string MailApiBCC { get; set; }
+
+ [IniProperty("OptimizePNGCommand", Description = "Optional command to execute on a temporary PNG file, the command should overwrite the file and Greenshot will read it back. Note: this command is also executed when uploading PNG's!", DefaultValue = "")]
+ public string OptimizePNGCommand { get; set; }
+ [IniProperty("OptimizePNGCommandArguments", Description = "Arguments for the optional command to execute on a PNG, {0} is replaced by the temp-filename from Greenshot. Note: Temp-file is deleted afterwards by Greenshot.", DefaultValue = "\"{0}\"")]
+ public string OptimizePNGCommandArguments { get; set; }
+
+ [IniProperty("LastSaveWithVersion", Description = "Version of Greenshot which created this .ini")]
+ public string LastSaveWithVersion { get; set; }
+
+ [IniProperty("ProcessEXIFOrientation", Description = "When reading images from files or clipboard, use the EXIF information to correct the orientation", DefaultValue = "True")]
+ public bool ProcessEXIFOrientation { get; set; }
+
+ [IniProperty("LastCapturedRegion", Description = "The last used region, for reuse in the capture last region")]
+ public Rectangle LastCapturedRegion { get; set; }
+
+ [IniProperty("Win10BorderCrop", Description = "The capture is cropped with these settings, e.g. when you don't want to color around it -1,-1"), DefaultValue("0,0")]
+ public Size Win10BorderCrop { get; set; }
+
+ private Size _iconSize;
+ [IniProperty("BaseIconSize", Description = "Defines the base size of the icons (e.g. for the buttons in the editor), default value 16,16 and it's scaled to the current DPI", DefaultValue = "16,16")]
+ public Size IconSize {
+ get {
+ return _iconSize;
+ }
+ set {
+ Size newSize = value;
+ if (newSize != Size.Empty) {
+ if (newSize.Width < 16) {
+ newSize.Width = 16;
+ } else if (newSize.Width > 256) {
+ newSize.Width = 256;
+ }
+ newSize.Width = (newSize.Width / 16) * 16;
+ if (newSize.Height < 16) {
+ newSize.Height = 16;
+ } else if (IconSize.Height > 256) {
+ newSize.Height = 256;
+ }
+ newSize.Height = (newSize.Height / 16) * 16;
+ }
+ if (_iconSize != newSize) {
+ _iconSize = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IconSize"));
+ }
+ }
+ }
+
+ public Size ScaledIconSize => DpiHelper.ScaleWithCurrentDpi(_iconSize);
+
+ [IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "100")]
+ public int WebRequestTimeout { get; set; }
+ [IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for webrequets, these are seconds", DefaultValue = "100")]
+ public int WebRequestReadWriteTimeout { get; set; }
+
+ ///
+ /// Specifies what THIS build is
+ ///
+ public BuildStates BuildState {
+ get {
+ string informationalVersion = Application.ProductVersion;
+ if (informationalVersion != null) {
+ if (informationalVersion.ToLowerInvariant().Contains("-rc")) {
+ return BuildStates.RELEASE_CANDIDATE;
+ }
+ if (informationalVersion.ToLowerInvariant().Contains("-unstable")) {
+ return BuildStates.UNSTABLE;
+ }
+ }
+ return BuildStates.RELEASE;
+ }
+ }
+
+ public bool UseLargeIcons => IconSize.Width >= 32 || IconSize.Height >= 32;
+
+ ///
+ /// A helper method which returns true if the supplied experimental feature is enabled
+ ///
+ ///
+ ///
+ public bool IsExperimentalFeatureEnabled(string experimentalFeature) {
+ return (ExperimentalFeatures != null && ExperimentalFeatures.Contains(experimentalFeature));
+ }
+
+ ///
+ /// Supply values we can't put as defaults
+ ///
+ /// The property to return a default for
+ /// object with the default value for the supplied property
+ public override object GetDefault(string property) {
+ switch(property) {
+ case "PluginWhitelist":
+ case "PluginBacklist":
+ return new List();
+ case "OutputFileAsFullpath":
+ if (IniConfig.IsPortable) {
+ return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png");
+ }
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),"dummy.png");
+ case "OutputFilePath":
+ if (IniConfig.IsPortable) {
+ string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots");
+ if (!Directory.Exists(pafOutputFilePath)) {
+ try {
+ Directory.CreateDirectory(pafOutputFilePath);
+ return pafOutputFilePath;
+ } catch (Exception ex) {
+ LOG.Warn(ex);
+ // Problem creating directory, fallback to Desktop
+ }
+ } else {
+ return pafOutputFilePath;
+ }
+ }
+ return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ case "DWMBackgroundColor":
+ return Color.Transparent;
+ case "ActiveTitleFixes":
+ return new List {"Firefox", "IE", "Chrome"};
+ case "TitleFixMatcher":
+ return new Dictionary {{"Firefox", " - Mozilla Firefox.*"}, {"IE", " - (Microsoft|Windows) Internet Explorer.*"}, {"Chrome", " - Google Chrome.*"}};
+ case "TitleFixReplacer":
+ return new Dictionary {{"Firefox", string.Empty }, {"IE", string.Empty }, {"Chrome", string.Empty } };
+ }
+ return null;
+ }
+
+ ///
+ /// This method will be called before converting the property, making to possible to correct a certain value
+ /// Can be used when migration is needed
+ ///
+ /// The name of the property
+ /// The string value of the property
+ /// string with the propertyValue, modified or not...
+ public override string PreCheckValue(string propertyName, string propertyValue) {
+ // Changed the separator, now we need to correct this
+ if ("Destinations".Equals(propertyName)) {
+ if (propertyValue != null) {
+ return propertyValue.Replace('|',',');
+ }
+ }
+ if("OutputFilePath".Equals(propertyName)) {
+ if (string.IsNullOrEmpty(propertyValue)) {
+ return null;
+ }
+ }
+ return base.PreCheckValue(propertyName, propertyValue);
+ }
+
+ ///
+ /// This method will be called before writing the configuration
+ ///
+ public override void BeforeSave() {
+ try {
+ // Store version, this can be used later to fix settings after an update
+ LastSaveWithVersion = Assembly.GetEntryAssembly().GetName().Version.ToString();
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ ///
+ /// This method will be called after reading the configuration, so eventually some corrections can be made
+ ///
+ public override void AfterLoad() {
+ // Comment with releases
+ // CheckForUnstable = true;
+
+ if (string.IsNullOrEmpty(LastSaveWithVersion)) {
+ try {
+ // Store version, this can be used later to fix settings after an update
+ LastSaveWithVersion = Assembly.GetEntryAssembly().GetName().Version.ToString();
+ }
+ catch
+ {
+ // ignored
+ }
+ // Disable the AutoReduceColors as it causes issues with Mozzila applications and some others
+ OutputFileAutoReduceColors = false;
+ }
+
+ // Fix for excessive feed checking
+ if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && LastSaveWithVersion.StartsWith("1.2"))
+ {
+ UpdateCheckInterval = 14;
+ }
+ if (UpdateCheckInterval > 365)
+ {
+ UpdateCheckInterval = 365;
+ }
+
+ // Enable OneNote if upgrading from 1.1
+ if (ExcludeDestinations != null && ExcludeDestinations.Contains("OneNote")) {
+ if(LastSaveWithVersion != null && LastSaveWithVersion.StartsWith("1.1")) {
+ ExcludeDestinations.Remove("OneNote");
+ } else {
+ // TODO: Remove with the release
+ ExcludeDestinations.Remove("OneNote");
+ }
+ }
+
+ if (OutputDestinations == null) {
+ OutputDestinations = new List();
+ }
+
+ // Make sure there is an output!
+ if (OutputDestinations.Count == 0) {
+ OutputDestinations.Add("Editor");
+ }
+
+ // Prevent both settings at once, bug #3435056
+ if (OutputDestinations.Contains("Clipboard") && OutputFileCopyPathToClipboard) {
+ OutputFileCopyPathToClipboard = false;
+ }
+
+ // Make sure we have clipboard formats, otherwise a paste doesn't make sense!
+ if (ClipboardFormats == null || ClipboardFormats.Count == 0) {
+ ClipboardFormats = new List {ClipboardFormat.PNG, ClipboardFormat.HTML, ClipboardFormat.DIB};
+ }
+
+ // Make sure the lists are lowercase, to speedup the check
+ if (NoGDICaptureForProduct != null) {
+ // Fix error in configuration
+ if (NoGDICaptureForProduct.Count >= 2) {
+ if ("intellij".Equals(NoGDICaptureForProduct[0]) && "idea".Equals(NoGDICaptureForProduct[1])) {
+ NoGDICaptureForProduct.RemoveRange(0, 2);
+ NoGDICaptureForProduct.Add("Intellij Idea");
+ IsDirty = true;
+ }
+ }
+ for (int i = 0; i < NoGDICaptureForProduct.Count; i++) {
+ NoGDICaptureForProduct[i] = NoGDICaptureForProduct[i].ToLower();
+ }
+ }
+ if (NoDWMCaptureForProduct != null) {
+ // Fix error in configuration
+ if (NoDWMCaptureForProduct.Count >= 3) {
+ if ("citrix".Equals(NoDWMCaptureForProduct[0]) && "ica".Equals(NoDWMCaptureForProduct[1]) && "client".Equals(NoDWMCaptureForProduct[2])) {
+ NoDWMCaptureForProduct.RemoveRange(0, 3);
+ NoDWMCaptureForProduct.Add("Citrix ICA Client");
+ IsDirty = true;
+ }
+ }
+ for (int i = 0; i < NoDWMCaptureForProduct.Count; i++) {
+ NoDWMCaptureForProduct[i] = NoDWMCaptureForProduct[i].ToLower();
+ }
+ }
+
+ if (AutoCropDifference < 0) {
+ AutoCropDifference = 0;
+ }
+ if (AutoCropDifference > 255) {
+ AutoCropDifference = 255;
+ }
+ if (OutputFileReduceColorsTo < 2) {
+ OutputFileReduceColorsTo = 2;
+ }
+ if (OutputFileReduceColorsTo > 256) {
+ OutputFileReduceColorsTo = 256;
+ }
+
+ if (WebRequestTimeout <= 10) {
+ WebRequestTimeout = 100;
+ }
+ if (WebRequestReadWriteTimeout < 1) {
+ WebRequestReadWriteTimeout = 100;
+ }
+ }
+
+ ///
+ /// Validate the OutputFilePath, and if this is not correct it will be set to the default
+ /// Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC)
+ ///
+ public void ValidateAndCorrectOutputFilePath()
+ {
+ if (!Directory.Exists(OutputFilePath))
+ {
+ OutputFilePath = GetDefault(nameof(OutputFilePath)) as string;
+ }
+ }
+ ///
+ /// Validate the OutputFileAsFullpath, and if this is not correct it will be set to the default
+ /// Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC)
+ ///
+ public void ValidateAndCorrectOutputFileAsFullpath()
+ {
+ var outputFilePath = Path.GetDirectoryName(OutputFileAsFullpath);
+ if (outputFilePath == null || (!File.Exists(OutputFileAsFullpath) && !Directory.Exists(outputFilePath)))
+ {
+ OutputFileAsFullpath = GetDefault(nameof(OutputFileAsFullpath)) as string;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/CredentialsHelper.cs b/GreenshotPlugin/Core/CredentialsHelper.cs
new file mode 100644
index 000000000..7732c4f46
--- /dev/null
+++ b/GreenshotPlugin/Core/CredentialsHelper.cs
@@ -0,0 +1,532 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// The following code comes from: http://www.developerfusion.com/code/4693/using-the-credential-management-api/
+ /// and is slightly modified so it works for us.
+ /// As the "Stored usernames and passwords" which can be accessed by: Start-> Run and type "Control keymgr.dll"
+ /// doesn't show all credentials use the tool here: http://www.microsoft.com/indonesia/msdn/credmgmt.aspx
+ /// The following code is an example for a login, it will call the Authenticate with user/password
+ /// which should return true if the login worked, false if not.
+ /// private static bool Login(string system, string name) {
+ /// try {
+ /// CredentialsDialog dialog = new CredentialsDialog(system);
+ /// dialog.Name = name;
+ /// while (dialog.Show(dialog.Name) == DialogResult.OK) {
+ /// if (Authenticate(dialog.Name, dialog.Password)) {
+ /// if (dialog.SaveChecked) dialog.Confirm(true);
+ /// return true;
+ /// } else {
+ /// try {
+ /// dialog.Confirm(false);
+ /// } catch (ApplicationException) {
+ /// // exception handling ...
+ /// }
+ /// dialog.IncorrectPassword = true;
+ /// }
+ /// }
+ /// } catch (ApplicationException) {
+ /// // exception handling ...
+ /// }
+ /// return false;
+ /// }
+ ///
+ /// Encapsulates dialog functionality from the Credential Management API.
+ public sealed class CredentialsDialog {
+ [DllImport("gdi32.dll", SetLastError=true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool DeleteObject(IntPtr hObject);
+
+ /// The only valid bitmap height (in pixels) of a user-defined banner.
+ private const int ValidBannerHeight = 60;
+
+ /// The only valid bitmap width (in pixels) of a user-defined banner.
+ private const int ValidBannerWidth = 320;
+
+ /// Initializes a new instance of the class
+ /// with the specified target.
+ /// The name of the target for the credentials, typically a server name.
+ public CredentialsDialog(string target) : this(target, null) {
+ }
+
+ /// Initializes a new instance of the class
+ /// with the specified target and caption.
+ /// The name of the target for the credentials, typically a server name.
+ /// The caption of the dialog (null will cause a system default title to be used).
+ public CredentialsDialog(string target, string caption) : this(target, caption, null) {
+ }
+
+ /// Initializes a new instance of the class
+ /// with the specified target, caption and message.
+ /// The name of the target for the credentials, typically a server name.
+ /// The caption of the dialog (null will cause a system default title to be used).
+ /// The message of the dialog (null will cause a system default message to be used).
+ public CredentialsDialog(string target, string caption, string message) : this(target, caption, message, null) {
+ }
+
+ /// Initializes a new instance of the class
+ /// with the specified target, caption, message and banner.
+ /// The name of the target for the credentials, typically a server name.
+ /// The caption of the dialog (null will cause a system default title to be used).
+ /// The message of the dialog (null will cause a system default message to be used).
+ /// The image to display on the dialog (null will cause a system default image to be used).
+ public CredentialsDialog(string target, string caption, string message, Image banner) {
+ Target = target;
+ Caption = caption;
+ Message = message;
+ Banner = banner;
+ }
+
+ ///
+ /// Gets or sets if the dialog will be shown even if the credentials
+ /// can be returned from an existing credential in the credential manager.
+ ///
+ public bool AlwaysDisplay { get; set; }
+
+ /// Gets or sets if the dialog is populated with name/password only.
+ public bool ExcludeCertificates { get; set; } = true;
+
+ /// Gets or sets if the credentials are to be persisted in the credential manager.
+ public bool Persist { get; set; } = true;
+
+ /// Gets or sets if the incorrect password balloontip needs to be shown. Introduced AFTER Windows XP
+ public bool IncorrectPassword { get; set; }
+
+ /// Gets or sets if the name is read-only.
+ public bool KeepName { get; set; }
+
+ private string _name = string.Empty;
+ /// Gets or sets the name for the credentials.
+ public string Name {
+ get {
+ return _name;
+ }
+ set {
+ if (value?.Length > CredUi.MAX_USERNAME_LENGTH) {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The name has a maximum length of {0} characters.",
+ CredUi.MAX_USERNAME_LENGTH);
+ throw new ArgumentException(message, nameof(Name));
+ }
+ _name = value;
+ }
+ }
+
+ private string _password = string.Empty;
+ /// Gets or sets the password for the credentials.
+ public string Password {
+ get {
+ return _password;
+ }
+ set {
+ if (value?.Length > CredUi.MAX_PASSWORD_LENGTH) {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The password has a maximum length of {0} characters.",
+ CredUi.MAX_PASSWORD_LENGTH);
+ throw new ArgumentException(message, nameof(Password));
+ }
+ _password = value;
+ }
+ }
+
+ /// Gets or sets if the save checkbox status.
+ public bool SaveChecked { get; set; }
+
+ /// Gets or sets if the save checkbox is displayed.
+ /// This value only has effect if Persist is true.
+ public bool SaveDisplayed { get; set; } = true;
+
+ private string _target = string.Empty;
+ /// Gets or sets the name of the target for the credentials, typically a server name.
+ public string Target {
+ get {
+ return _target;
+ }
+ set {
+ if (value == null) {
+ throw new ArgumentException("The target cannot be a null value.", nameof(Target));
+ }
+ if (value.Length > CredUi.MAX_GENERIC_TARGET_LENGTH) {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The target has a maximum length of {0} characters.",
+ CredUi.MAX_GENERIC_TARGET_LENGTH);
+ throw new ArgumentException(message, nameof(Target));
+ }
+ _target = value;
+ }
+ }
+
+ private string _caption = string.Empty;
+ /// Gets or sets the caption of the dialog.
+ /// A null value will cause a system default caption to be used.
+ public string Caption {
+ get {
+ return _caption;
+ }
+ set {
+ if (value?.Length > CredUi.MAX_CAPTION_LENGTH) {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The caption has a maximum length of {0} characters.",
+ CredUi.MAX_CAPTION_LENGTH);
+ throw new ArgumentException(message, nameof(Caption));
+ }
+ _caption = value;
+ }
+ }
+
+ private string _message = string.Empty;
+ /// Gets or sets the message of the dialog.
+ /// A null value will cause a system default message to be used.
+ public string Message {
+ get {
+ return _message;
+ }
+ set {
+ if (value?.Length > CredUi.MAX_MESSAGE_LENGTH) {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The message has a maximum length of {0} characters.",
+ CredUi.MAX_MESSAGE_LENGTH);
+ throw new ArgumentException(message, nameof(Message));
+ }
+ _message = value;
+ }
+ }
+
+ private Image _banner;
+ /// Gets or sets the image to display on the dialog.
+ /// A null value will cause a system default image to be used.
+ public Image Banner {
+ get {
+ return _banner;
+ }
+ set {
+ if (value != null) {
+ if (value.Width != ValidBannerWidth) {
+ throw new ArgumentException("The banner image width must be 320 pixels.", nameof(Banner));
+ }
+ if (value.Height != ValidBannerHeight) {
+ throw new ArgumentException("The banner image height must be 60 pixels.", nameof(Banner));
+ }
+ }
+ _banner = value;
+ }
+ }
+
+ /// Shows the credentials dialog.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show() {
+ return Show(null, Name, Password, SaveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified save checkbox status.
+ /// True if the save checkbox is checked.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(bool saveChecked) {
+ return Show(null, Name, Password, saveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified name.
+ /// The name for the credentials.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(string name) {
+ return Show(null, name, Password, SaveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified name and password.
+ /// The name for the credentials.
+ /// The password for the credentials.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(string name, string password) {
+ return Show(null, name, password, SaveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified name, password and save checkbox status.
+ /// The name for the credentials.
+ /// The password for the credentials.
+ /// True if the save checkbox is checked.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(string name, string password, bool saveChecked) {
+ return Show(null, name, password, saveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified owner.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(IWin32Window owner) {
+ return Show(owner, Name, Password, SaveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified owner and save checkbox status.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ /// True if the save checkbox is checked.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(IWin32Window owner, bool saveChecked) {
+ return Show(owner, Name, Password, saveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified owner, name and password.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ /// The name for the credentials.
+ /// The password for the credentials.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(IWin32Window owner, string name, string password) {
+ return Show(owner, name, password, SaveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified owner, name, password and save checkbox status.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ /// The name for the credentials.
+ /// The password for the credentials.
+ /// True if the save checkbox is checked.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(IWin32Window owner, string name, string password, bool saveChecked) {
+ if ((Environment.OSVersion.Version.Major < 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor < 1))) {
+ throw new ApplicationException("The Credential Management API requires Windows XP / Windows Server 2003 or later.");
+ }
+ Name = name;
+ Password = password;
+ SaveChecked = saveChecked;
+
+ return ShowDialog(owner);
+ }
+
+ /// Confirmation action to be applied.
+ /// True if the credentials should be persisted.
+ public void Confirm(bool value)
+ {
+ var confirmResult = CredUi.CredUIConfirmCredentials(Target, value);
+ switch (confirmResult) {
+ case CredUi.ReturnCodes.NO_ERROR:
+ break;
+ case CredUi.ReturnCodes.ERROR_INVALID_PARAMETER:
+ // for some reason, this is encountered when credentials are overwritten
+ break;
+ default:
+ throw new ApplicationException($"Credential confirmation failed: {confirmResult}");
+ }
+ }
+
+ /// Returns a DialogResult indicating the user action.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ ///
+ /// Sets the name, password and SaveChecked accessors to the state of the dialog as it was dismissed by the user.
+ ///
+ private DialogResult ShowDialog(IWin32Window owner) {
+ // set the api call parameters
+ StringBuilder name = new StringBuilder(CredUi.MAX_USERNAME_LENGTH);
+ name.Append(Name);
+
+ StringBuilder password = new StringBuilder(CredUi.MAX_PASSWORD_LENGTH);
+ password.Append(Password);
+
+ int saveChecked = Convert.ToInt32(SaveChecked);
+
+ CredUi.INFO info = GetInfo(owner);
+ CredUi.CredFlags credFlags = GetFlags();
+
+ // make the api call
+ CredUi.ReturnCodes code = CredUi.CredUIPromptForCredentials(
+ ref info,
+ Target,
+ IntPtr.Zero, 0,
+ name, CredUi.MAX_USERNAME_LENGTH,
+ password, CredUi.MAX_PASSWORD_LENGTH,
+ ref saveChecked,
+ credFlags
+ );
+
+ // clean up resources
+ if (Banner != null) {
+ DeleteObject(info.hbmBanner);
+ }
+
+ // set the accessors from the api call parameters
+ Name = name.ToString();
+ Password = password.ToString();
+ SaveChecked = Convert.ToBoolean(saveChecked);
+
+ return GetDialogResult(code);
+ }
+
+ /// Returns the info structure for dialog display settings.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ private CredUi.INFO GetInfo(IWin32Window owner) {
+ CredUi.INFO info = new CredUi.INFO();
+ if (owner != null) info.hwndParent = owner.Handle;
+ info.pszCaptionText = Caption;
+ info.pszMessageText = Message;
+ if (Banner != null) {
+ info.hbmBanner = new Bitmap(Banner, ValidBannerWidth, ValidBannerHeight).GetHbitmap();
+ }
+ info.cbSize = Marshal.SizeOf(info);
+ return info;
+ }
+
+ /// Returns the flags for dialog display options.
+ private CredUi.CredFlags GetFlags() {
+ CredUi.CredFlags credFlags = CredUi.CredFlags.GENERIC_CREDENTIALS;
+
+ if (IncorrectPassword) {
+ credFlags |= CredUi.CredFlags.INCORRECT_PASSWORD;
+ }
+
+ if (AlwaysDisplay) {
+ credFlags |= CredUi.CredFlags.ALWAYS_SHOW_UI;
+ }
+
+ if (ExcludeCertificates) {
+ credFlags |= CredUi.CredFlags.EXCLUDE_CERTIFICATES;
+ }
+
+ if (Persist) {
+ credFlags |= CredUi.CredFlags.EXPECT_CONFIRMATION;
+ if (SaveDisplayed) {
+ credFlags |= CredUi.CredFlags.SHOW_SAVE_CHECK_BOX;
+ } else {
+ credFlags |= CredUi.CredFlags.PERSIST;
+ }
+ } else {
+ credFlags |= CredUi.CredFlags.DO_NOT_PERSIST;
+ }
+
+ if (KeepName) {
+ credFlags |= CredUi.CredFlags.KEEP_USERNAME;
+ }
+
+ return credFlags;
+ }
+
+ /// Returns a DialogResult from the specified code.
+ /// The credential return code.
+ private DialogResult GetDialogResult(CredUi.ReturnCodes code) =>
+ code switch
+ {
+ CredUi.ReturnCodes.NO_ERROR => DialogResult.OK,
+ CredUi.ReturnCodes.ERROR_CANCELLED => DialogResult.Cancel,
+ CredUi.ReturnCodes.ERROR_NO_SUCH_LOGON_SESSION => throw new ApplicationException(
+ "No such logon session."),
+ CredUi.ReturnCodes.ERROR_NOT_FOUND => throw new ApplicationException("Not found."),
+ CredUi.ReturnCodes.ERROR_INVALID_ACCOUNT_NAME =>
+ throw new ApplicationException("Invalid account name."),
+ CredUi.ReturnCodes.ERROR_INSUFFICIENT_BUFFER => throw new ApplicationException("Insufficient buffer."),
+ CredUi.ReturnCodes.ERROR_INVALID_PARAMETER => throw new ApplicationException("Invalid parameter."),
+ CredUi.ReturnCodes.ERROR_INVALID_FLAGS => throw new ApplicationException("Invalid flags."),
+ _ => throw new ApplicationException("Unknown credential result encountered.")
+ };
+ }
+
+ internal static class CredUi {
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/authentication_constants.asp
+ public const int MAX_MESSAGE_LENGTH = 100;
+ public const int MAX_CAPTION_LENGTH = 100;
+ public const int MAX_GENERIC_TARGET_LENGTH = 100;
+ public const int MAX_DOMAIN_TARGET_LENGTH = 100;
+ public const int MAX_USERNAME_LENGTH = 100;
+ public const int MAX_PASSWORD_LENGTH = 100;
+
+ ///
+ /// http://www.pinvoke.net/default.aspx/Enums.CREDUI_FLAGS
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/dpapiusercredentials.asp
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp
+ ///
+ [Flags]
+ public enum CredFlags {
+ INCORRECT_PASSWORD = 0x1,
+ DO_NOT_PERSIST = 0x2,
+ REQUEST_ADMINISTRATOR = 0x4,
+ EXCLUDE_CERTIFICATES = 0x8,
+ REQUIRE_CERTIFICATE = 0x10,
+ SHOW_SAVE_CHECK_BOX = 0x40,
+ ALWAYS_SHOW_UI = 0x80,
+ REQUIRE_SMARTCARD = 0x100,
+ PASSWORD_ONLY_OK = 0x200,
+ VALIDATE_USERNAME = 0x400,
+ COMPLETE_USERNAME = 0x800,
+ PERSIST = 0x1000,
+ SERVER_CREDENTIAL = 0x4000,
+ EXPECT_CONFIRMATION = 0x20000,
+ GENERIC_CREDENTIALS = 0x40000,
+ USERNAME_TARGET_CREDENTIALS = 0x80000,
+ KEEP_USERNAME = 0x100000,
+ }
+
+ /// http://www.pinvoke.net/default.aspx/Enums.CredUIReturnCodes
+ public enum ReturnCodes {
+ NO_ERROR = 0,
+ ERROR_INVALID_PARAMETER = 87,
+ ERROR_INSUFFICIENT_BUFFER = 122,
+ ERROR_INVALID_FLAGS = 1004,
+ ERROR_NOT_FOUND = 1168,
+ ERROR_CANCELLED = 1223,
+ ERROR_NO_SUCH_LOGON_SESSION = 1312,
+ ERROR_INVALID_ACCOUNT_NAME = 1315
+ }
+
+ ///
+ /// http://www.pinvoke.net/default.aspx/Structures.CREDUI_INFO
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/credui_info.asp
+ ///
+ public struct INFO {
+ public int cbSize;
+ public IntPtr hwndParent;
+ [MarshalAs(UnmanagedType.LPWStr)] public string pszMessageText;
+ [MarshalAs(UnmanagedType.LPWStr)] public string pszCaptionText;
+ public IntPtr hbmBanner;
+ }
+
+ ///
+ /// http://www.pinvoke.net/default.aspx/credui.CredUIPromptForCredentialsW
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp
+ ///
+ [DllImport("credui", CharSet = CharSet.Unicode)]
+ public static extern ReturnCodes CredUIPromptForCredentials (
+ ref INFO creditUR,
+ string targetName,
+ IntPtr reserved1,
+ int iError,
+ StringBuilder userName,
+ int maxUserName,
+ StringBuilder password,
+ int maxPassword,
+ ref int iSave,
+ CredFlags credFlags
+ );
+
+ ///
+ /// http://www.pinvoke.net/default.aspx/credui.CredUIConfirmCredentials
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduiconfirmcredentials.asp
+ ///
+ [DllImport("credui.dll", CharSet=CharSet.Unicode)]
+ public static extern ReturnCodes CredUIConfirmCredentials(string targetName, [MarshalAs(UnmanagedType.Bool)] bool confirm);
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/DisplayKeyAttribute.cs b/GreenshotPlugin/Core/DisplayKeyAttribute.cs
new file mode 100644
index 000000000..d055b91ad
--- /dev/null
+++ b/GreenshotPlugin/Core/DisplayKeyAttribute.cs
@@ -0,0 +1,35 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+
+namespace GreenshotPlugin.Core {
+ [AttributeUsage(AttributeTargets.Field)]
+ public sealed class DisplayKeyAttribute : Attribute {
+ public string Value { get; }
+
+ public DisplayKeyAttribute(string v) {
+ Value = v;
+ }
+
+ public DisplayKeyAttribute() {
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/DpiHelper.cs b/GreenshotPlugin/Core/DpiHelper.cs
new file mode 100644
index 000000000..2c67eb1b5
--- /dev/null
+++ b/GreenshotPlugin/Core/DpiHelper.cs
@@ -0,0 +1,709 @@
+using GreenshotPlugin.Core.Enums;
+using GreenshotPlugin.UnmanagedHelpers;
+using log4net;
+using System;
+using System.Diagnostics;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using GreenshotPlugin.UnmanagedHelpers.Enums;
+using GreenshotPlugin.UnmanagedHelpers.Structs;
+
+namespace GreenshotPlugin.Core
+{
+ ///
+ /// This handles DPI changes see
+ /// Writing DPI-Aware Desktop and Win32 Applications
+ ///
+ public static class DpiHelper
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(DpiHelper));
+
+ ///
+ /// This is the default DPI for the screen
+ ///
+ public const uint DefaultScreenDpi = 96;
+
+ ///
+ /// Retrieve the current DPI for the UI element which is related to this DpiHandler
+ ///
+ public static uint Dpi { get; private set; } = WindowsVersion.IsWindows10OrLater ? GetDpiForSystem() : DefaultScreenDpi;
+
+ ///
+ /// Calculate a DPI scale factor
+ ///
+ /// uint
+ /// double
+ public static float DpiScaleFactor(uint dpi)
+ {
+ return (float)dpi / DefaultScreenDpi;
+ }
+
+ ///
+ /// Scale the supplied number according to the supplied dpi
+ ///
+ /// double with e.g. the width 16 for 16x16 images
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// double with the scaled number
+ public static float ScaleWithDpi(float someNumber, uint dpi, Func scaleModifier = null)
+ {
+ var dpiScaleFactor = DpiScaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiScaleFactor = scaleModifier(dpiScaleFactor);
+ }
+ return dpiScaleFactor * someNumber;
+ }
+
+ ///
+ /// Scale the supplied number according to the supplied dpi
+ ///
+ /// int with e.g. 16 for 16x16 images
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// Scaled width
+ public static int ScaleWithDpi(int number, uint dpi, Func scaleModifier = null)
+ {
+ var dpiScaleFactor = DpiScaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiScaleFactor = scaleModifier(dpiScaleFactor);
+ }
+ return (int)(dpiScaleFactor * number);
+ }
+
+ ///
+ /// Scale the supplied Size according to the supplied dpi
+ ///
+ /// Size to resize
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// NativeSize scaled
+ public static Size ScaleWithDpi(Size size, uint dpi, Func scaleModifier = null)
+ {
+ var dpiScaleFactor = DpiScaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiScaleFactor = scaleModifier(dpiScaleFactor);
+ }
+ return new Size((int)(dpiScaleFactor * size.Width), (int)(dpiScaleFactor * size.Height));
+ }
+
+ ///
+ /// Scale the supplied NativePoint according to the supplied dpi
+ ///
+ /// NativePoint to resize
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// NativePoint scaled
+ public static Point ScaleWithDpi(Point size, uint dpi, Func scaleModifier = null)
+ {
+ var dpiScaleFactor = DpiScaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiScaleFactor = scaleModifier(dpiScaleFactor);
+ }
+ return new Point((int)(dpiScaleFactor * size.X), (int)(dpiScaleFactor * size.Y));
+ }
+
+ ///
+ /// Scale the supplied NativeSizeFloat according to the supplied dpi
+ ///
+ /// PointF
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// PointF
+ public static PointF ScaleWithDpi(PointF point, uint dpi, Func scaleModifier = null)
+ {
+ var dpiScaleFactor = DpiScaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiScaleFactor = scaleModifier(dpiScaleFactor);
+ }
+ return new PointF(dpiScaleFactor * point.X, dpiScaleFactor * point.Y);
+ }
+
+ ///
+ /// Scale the supplied NativeSizeFloat according to the supplied dpi
+ ///
+ /// NativeSizeFloat to resize
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// NativeSize scaled
+ public static SizeF ScaleWithDpi(SizeF size, uint dpi, Func scaleModifier = null)
+ {
+ var dpiScaleFactor = DpiScaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiScaleFactor = scaleModifier(dpiScaleFactor);
+ }
+ return new SizeF(dpiScaleFactor * size.Width, dpiScaleFactor * size.Height);
+ }
+
+ ///
+ /// Scale the supplied number to the current dpi
+ ///
+ /// double with e.g. a width like 16 for 16x16 images
+ /// A function which can modify the scale factor
+ /// double with scaled number
+ public static float ScaleWithCurrentDpi(float someNumber, Func scaleModifier = null)
+ {
+ return ScaleWithDpi(someNumber, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Scale the supplied number to the current dpi
+ ///
+ /// int with e.g. a width like 16 for 16x16 images
+ /// A function which can modify the scale factor
+ /// int with scaled number
+ public static int ScaleWithCurrentDpi(int someNumber, Func scaleModifier = null)
+ {
+ return ScaleWithDpi(someNumber, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Scale the supplied NativeSize to the current dpi
+ ///
+ /// NativeSize to scale
+ /// A function which can modify the scale factor
+ /// NativeSize scaled
+ public static Size ScaleWithCurrentDpi(Size size, Func scaleModifier = null)
+ {
+ return ScaleWithDpi(size, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Scale the supplied NativeSizeFloat to the current dpi
+ ///
+ /// NativeSizeFloat to scale
+ /// A function which can modify the scale factor
+ /// NativeSizeFloat scaled
+ public static SizeF ScaleWithCurrentDpi(SizeF size, Func scaleModifier = null)
+ {
+ return ScaleWithDpi(size, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Scale the supplied NativePoint to the current dpi
+ ///
+ /// NativePoint to scale
+ /// A function which can modify the scale factor
+ /// NativePoint scaled
+ public static Point ScaleWithCurrentDpi(Point point, Func scaleModifier = null)
+ {
+ return ScaleWithDpi(point, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Scale the supplied PointF to the current dpi
+ ///
+ /// PointF to scale
+ /// A function which can modify the scale factor
+ /// PointF scaled
+ public static PointF ScaleWithCurrentDpi(PointF point, Func scaleModifier = null)
+ {
+ return ScaleWithDpi(point, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Calculate a DPI unscale factor
+ ///
+ /// uint
+ /// float
+ public static float DpiUnscaleFactor(uint dpi)
+ {
+ return (float)DefaultScreenDpi / dpi;
+ }
+
+ ///
+ /// Unscale the supplied number according to the supplied dpi
+ ///
+ /// double with e.g. the scaled width
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// double with the unscaled number
+ public static float UnscaleWithDpi(float someNumber, uint dpi, Func scaleModifier = null)
+ {
+ var dpiUnscaleFactor = DpiUnscaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
+ }
+ return dpiUnscaleFactor * someNumber;
+ }
+
+ ///
+ /// Unscale the supplied number according to the supplied dpi
+ ///
+ /// int with a scaled width
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// Unscaled width
+ public static int UnscaleWithDpi(int number, uint dpi, Func scaleModifier = null)
+ {
+ var dpiUnscaleFactor = DpiUnscaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
+ }
+ return (int)(dpiUnscaleFactor * number);
+ }
+
+ ///
+ /// Unscale the supplied NativeSize according to the supplied dpi
+ ///
+ /// NativeSize to unscale
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// Size unscaled
+ public static Size UnscaleWithDpi(Size size, uint dpi, Func scaleModifier = null)
+ {
+ var dpiUnscaleFactor = DpiUnscaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
+ }
+ return new Size((int)(dpiUnscaleFactor * size.Width), (int)(dpiUnscaleFactor * size.Height));
+ }
+
+ ///
+ /// Unscale the supplied Point according to the supplied dpi
+ ///
+ /// Point to unscale
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// Point unscaled
+ public static Point UnscaleWithDpi(Point point, uint dpi, Func scaleModifier = null)
+ {
+ var dpiUnscaleFactor = DpiUnscaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
+ }
+ return new Point((int)(dpiUnscaleFactor * point.X), (int)(dpiUnscaleFactor * point.Y));
+ }
+
+ ///
+ /// unscale the supplied NativeSizeFloat according to the supplied dpi
+ ///
+ /// NativeSizeFloat to resize
+ /// current dpi, normal is 96.
+ /// A function which can modify the scale factor
+ /// SizeF unscaled
+ public static SizeF UnscaleWithDpi(SizeF size, uint dpi, Func scaleModifier = null)
+ {
+ float dpiUnscaleFactor = DpiUnscaleFactor(dpi);
+ if (scaleModifier != null)
+ {
+ dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
+ }
+ return new SizeF(dpiUnscaleFactor * size.Width, dpiUnscaleFactor * size.Height);
+ }
+
+ ///
+ /// Unscale the supplied number to the current dpi
+ ///
+ /// double with e.g. a width like 16 for 16x16 images
+ /// A function which can modify the scale factor
+ /// double with unscaled number
+ public static float UnscaleWithCurrentDpi(float someNumber, Func scaleModifier = null)
+ {
+ return UnscaleWithDpi(someNumber, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Unscale the supplied number to the current dpi
+ ///
+ /// int with e.g. a width like 16 for 16x16 images
+ /// A function which can modify the scale factor
+ /// int with unscaled number
+ public static int UnscaleWithCurrentDpi(int someNumber, Func scaleModifier = null)
+ {
+ return UnscaleWithDpi(someNumber, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Unscale the supplied NativeSize to the current dpi
+ ///
+ /// Size to unscale
+ /// A function which can modify the scale factor
+ /// Size unscaled
+ public static Size UnscaleWithCurrentDpi(Size size, Func scaleModifier = null)
+ {
+ return UnscaleWithDpi(size, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Unscale the supplied NativeSizeFloat to the current dpi
+ ///
+ /// NativeSizeFloat to unscale
+ /// A function which can modify the scale factor
+ /// NativeSizeFloat unscaled
+ public static SizeF UnscaleWithCurrentDpi(SizeF size, Func scaleModifier = null)
+ {
+ return UnscaleWithDpi(size, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Unscale the supplied NativePoint to the current dpi
+ ///
+ /// NativePoint to unscale
+ /// A function which can modify the scale factor
+ /// NativePoint unscaled
+ public static Point UnscaleWithCurrentDpi(Point point, Func scaleModifier = null)
+ {
+ return UnscaleWithDpi(point, Dpi, scaleModifier);
+ }
+
+ ///
+ /// Unscale the supplied NativePointFloat to the current dpi
+ ///
+ /// NativePointFloat to unscale
+ /// A function which can modify the scale factor
+ /// NativePointFloat unscaled
+ public static PointF UnscaleWithCurrentDpi(PointF point, Func scaleModifier = null)
+ {
+ return ScaleWithDpi(point, Dpi, scaleModifier);
+ }
+
+ ///
+ /// public wrapper for EnableNonClientDpiScaling, this also checks if the function is available.
+ ///
+ /// IntPtr
+ /// true if it worked
+ public static bool TryEnableNonClientDpiScaling(IntPtr hWnd)
+ {
+ // EnableNonClientDpiScaling is only available on Windows 10 and later
+ if (!WindowsVersion.IsWindows10OrLater)
+ {
+ return false;
+ }
+
+ var result = EnableNonClientDpiScaling(hWnd);
+ if (result.Succeeded())
+ {
+ return true;
+ }
+
+ var error = Win32.GetLastErrorCode();
+ if (Log.IsDebugEnabled)
+ {
+ Log.DebugFormat("Error enabling non client dpi scaling : {0}", Win32.GetMessage(error));
+ }
+
+ return false;
+ }
+
+ ///
+ /// Make the current process DPI Aware, this should be done via the manifest but sometimes this is not possible.
+ ///
+ /// bool true if it was possible to change the DPI awareness
+ public static bool EnableDpiAware()
+ {
+ // We can only test this for Windows 8.1 or later
+ if (!WindowsVersion.IsWindows81OrLater)
+ {
+ Log.Debug("An application can only be DPI aware starting with Window 8.1 and later.");
+ return false;
+ }
+
+ if (WindowsVersion.IsWindows10BuildOrLater(15063))
+ {
+ if (IsValidDpiAwarenessContext(DpiAwarenessContext.PerMonitorAwareV2))
+ {
+ SetProcessDpiAwarenessContext(DpiAwarenessContext.PerMonitorAwareV2);
+ }
+ else
+ {
+ SetProcessDpiAwarenessContext(DpiAwarenessContext.PerMonitorAwareV2);
+ }
+
+ return true;
+ }
+ return SetProcessDpiAwareness(DpiAwareness.PerMonitorAware).Succeeded();
+ }
+
+ ///
+ /// Check if the process is DPI Aware, an DpiHandler doesn't make sense if not.
+ ///
+ public static bool IsDpiAware
+ {
+ get
+ {
+ // We can only test this for Windows 8.1 or later
+ if (!WindowsVersion.IsWindows81OrLater)
+ {
+ Log.Debug("An application can only be DPI aware starting with Window 8.1 and later.");
+ return false;
+ }
+
+ using var process = Process.GetCurrentProcess();
+ GetProcessDpiAwareness(process.Handle, out var dpiAwareness);
+ if (Log.IsDebugEnabled)
+ {
+ Log.DebugFormat("Process {0} has a Dpi awareness {1}", process.ProcessName, dpiAwareness);
+ }
+
+ return dpiAwareness != DpiAwareness.Unaware && dpiAwareness != DpiAwareness.Invalid;
+ }
+ }
+
+ ///
+ /// Retrieve the DPI value for the supplied window handle
+ ///
+ /// IntPtr
+ /// dpi value
+ public static uint GetDpi(IntPtr hWnd)
+ {
+ if (!User32.IsWindow(hWnd))
+ {
+ return DefaultScreenDpi;
+ }
+
+ // Use the easiest method, but this only works for Windows 10
+ if (WindowsVersion.IsWindows10OrLater)
+ {
+ return GetDpiForWindow(hWnd);
+ }
+
+ // Use the second easiest method, but this only works for Windows 8.1 or later
+ if (WindowsVersion.IsWindows81OrLater)
+ {
+ var hMonitor = User32.MonitorFromWindow(hWnd, MonitorFrom.DefaultToNearest);
+ // ReSharper disable once UnusedVariable
+ if (GetDpiForMonitor(hMonitor, MonitorDpiType.EffectiveDpi, out var dpiX, out var dpiY))
+ {
+ return dpiX;
+ }
+ }
+
+ // Fallback to the global DPI settings
+ using var hdc = SafeWindowDcHandle.FromWindow(hWnd);
+ if (hdc == null)
+ {
+ return DefaultScreenDpi;
+ }
+ return (uint)GDI32.GetDeviceCaps(hdc, DeviceCaps.LOGPIXELSX);
+ }
+
+ ///
+ /// See details GetProcessDpiAwareness function
+ /// Retrieves the dots per inch (dpi) awareness of the specified process.
+ ///
+ /// IntPtr with handle of the process that is being queried. If this parameter is NULL, the current process is queried.
+ /// out DpiAwareness - The DPI awareness of the specified process. Possible values are from the PROCESS_DPI_AWARENESS enumeration.
+ /// HResult
+ [DllImport("shcore")]
+ private static extern HResult GetProcessDpiAwareness(IntPtr processHandle, out DpiAwareness value);
+
+ ///
+ /// Sets the current process to a specified dots per inch (dpi) awareness level. The DPI awareness levels are from the PROCESS_DPI_AWARENESS enumeration.
+ /// See SetProcessDpiAwareness function
+ ///
+ /// DpiAwareness
+ /// HResult
+ [DllImport("shcore")]
+ private static extern HResult SetProcessDpiAwareness(DpiAwareness dpiAwareness);
+
+ ///
+ /// It is recommended that you set the process-default DPI awareness via application manifest. See Setting the default DPI awareness for a process for more information. Setting the process-default DPI awareness via API call can lead to unexpected application behavior.
+ ///
+ /// Sets the current process to a specified dots per inch (dpi) awareness context. The DPI awareness contexts are from the DPI_AWARENESS_CONTEXT value.
+ /// Remarks:
+ /// This API is a more advanced version of the previously existing SetProcessDpiAwareness API, allowing for the process default to be set to the finer-grained DPI_AWARENESS_CONTEXT values. Most importantly, this allows you to programmatically set Per Monitor v2 as the process default value, which is not possible with the previous API.
+ ///
+ /// This method sets the default DPI_AWARENESS_CONTEXT for all threads within an application. Individual threads can have their DPI awareness changed from the default with the SetThreadDpiAwarenessContext method.
+ /// See SetProcessDpiAwarenessContext function
+ ///
+ /// DpiAwarenessContext
+ /// bool
+ [DllImport("User32.dll", SetLastError = true)]
+ private static extern bool SetProcessDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
+
+ ///
+ /// See more at GetDpiForWindow function
+ /// Returns the dots per inch (dpi) value for the associated window.
+ ///
+ /// IntPtr
+ /// uint with dpi
+ [DllImport("User32.dll")]
+ private static extern uint GetDpiForWindow(IntPtr hWnd);
+
+ ///
+ /// See
+ /// GetDpiForMonitor function
+ /// Queries the dots per inch (dpi) of a display.
+ ///
+ /// IntPtr
+ /// MonitorDpiType
+ /// out int for the horizontal dpi
+ /// out int for the vertical dpi
+ /// true if all okay
+ [DllImport("shcore")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool GetDpiForMonitor(IntPtr hMonitor, MonitorDpiType dpiType, out uint dpiX, out uint dpiY);
+
+ ///
+ /// See EnableNonClientDpiScaling function
+ ///
+ /// IntPtr
+ /// bool
+ [DllImport("User32.dll", SetLastError = true)]
+ private static extern HResult EnableNonClientDpiScaling(IntPtr hWnd);
+
+ ///
+ /// See GetDpiForSystem function
+ /// Returns the system DPI.
+ ///
+ /// uint with the system DPI
+ [DllImport("User32.dll")]
+ private static extern uint GetDpiForSystem();
+
+ ///
+ /// Converts a point in a window from logical coordinates into physical coordinates, regardless of the dots per inch (dpi) awareness of the caller. For more information about DPI awareness levels, see PROCESS_DPI_AWARENESS.
+ /// See more at LogicalToPhysicalPointForPerMonitorDPI function
+ ///
+ /// IntPtr A handle to the window whose transform is used for the conversion.
+ /// A pointer to a POINT structure that specifies the logical coordinates to be converted. The new physical coordinates are copied into this structure if the function succeeds.
+ /// bool
+ [DllImport("User32.dll")]
+ private static extern bool LogicalToPhysicalPointForPerMonitorDPI(IntPtr hWnd, ref POINT point);
+
+ ///
+ /// Converts a point in a window from logical coordinates into physical coordinates, regardless of the dots per inch (dpi) awareness of the caller. For more information about DPI awareness levels, see PROCESS_DPI_AWARENESS.
+ /// See more at PhysicalToLogicalPointForPerMonitorDPI function
+ ///
+ /// IntPtr A handle to the window whose transform is used for the conversion.
+ /// NativePoint A pointer to a POINT structure that specifies the physical/screen coordinates to be converted. The new logical coordinates are copied into this structure if the function succeeds.
+ /// bool
+ [DllImport("User32.dll")]
+ private static extern bool PhysicalToLogicalPointForPerMonitorDPI(IntPtr hWnd, ref POINT point);
+
+ ///
+ /// See SystemParametersInfo function
+ /// Retrieves the value of one of the system-wide parameters, taking into account the provided DPI value.
+ ///
+ ///
+ /// SystemParametersInfoActions The system-wide parameter to be retrieved.
+ /// This function is only intended for use with SPI_GETICONTITLELOGFONT, SPI_GETICONMETRICS, or SPI_GETNONCLIENTMETRICS. See SystemParametersInfo for more information on these values.
+ ///
+ ///
+ /// A parameter whose usage and format depends on the system parameter being queried or set. For more
+ /// information about system-wide parameters, see the uiAction parameter. If not otherwise indicated, you must specify
+ /// zero for this parameter.
+ ///
+ /// IntPtr
+ /// SystemParametersInfoBehaviors
+ /// uint with dpi value
+ /// bool
+ [DllImport("User32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool SystemParametersInfoForDpi(SystemParametersInfoActions uiAction, uint uiParam, IntPtr pvParam, SystemParametersInfoBehaviors fWinIni, uint dpi);
+
+ ///
+ /// See GetThreadDpiAwarenessContext function
+ /// Gets the DPI_AWARENESS_CONTEXT for the current thread.
+ ///
+ /// This method will return the latest DPI_AWARENESS_CONTEXT sent to SetThreadDpiAwarenessContext. If SetThreadDpiAwarenessContext was never called for this thread, then the return value will equal the default DPI_AWARENESS_CONTEXT for the process.
+ ///
+ /// DpiAwarenessContext
+ [DllImport("User32.dll")]
+ private static extern DpiAwarenessContext GetThreadDpiAwarenessContext();
+
+ ///
+ /// Set the DPI awareness for the current thread to the provided value.
+ ///
+ /// DpiAwarenessContext the new value for the current thread
+ /// DpiAwarenessContext previous value
+ [DllImport("User32.dll")]
+ private static extern DpiAwarenessContext SetThreadDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
+
+ ///
+ /// Retrieves the DpiAwareness value from a DpiAwarenessContext.
+ ///
+ /// DpiAwarenessContext
+ /// DpiAwareness
+ [DllImport("User32.dll")]
+ private static extern DpiAwareness GetAwarenessFromDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
+
+ ///
+ /// Retrieves the DPI from a given DPI_AWARENESS_CONTEXT handle. This enables you to determine the DPI of a thread without needed to examine a window created within that thread.
+ ///
+ /// DpiAwarenessContext
+ /// uint with dpi value
+ [DllImport("User32.dll")]
+ private static extern uint GetDpiFromDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
+
+ ///
+ /// Determines if a specified DPI_AWARENESS_CONTEXT is valid and supported by the current system.
+ ///
+ /// DpiAwarenessContext The context that you want to determine if it is supported.
+ /// bool true if supported otherwise false
+ [DllImport("User32.dll")]
+ private static extern bool IsValidDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
+
+ ///
+ /// Returns the DPI_HOSTING_BEHAVIOR of the specified window.
+ ///
+ /// This API allows you to examine the hosting behavior of a window after it has been created. A window's hosting behavior is the hosting behavior of the thread in which the window was created, as set by a call to SetThreadDpiHostingBehavior. This is a permanent value and cannot be changed after the window is created, even if the thread's hosting behavior is changed.
+ ///
+ /// DpiHostingBehavior
+ [DllImport("User32.dll")]
+ private static extern DpiHostingBehavior GetWindowDpiHostingBehavior();
+
+ ///
+ /// See more at SetThreadDpiHostingBehavior function
+ /// Sets the thread's DPI_HOSTING_BEHAVIOR. This behavior allows windows created in the thread to host child windows with a different DPI_AWARENESS_CONTEXT.
+ ///
+ /// DPI_HOSTING_BEHAVIOR enables a mixed content hosting behavior, which allows parent windows created in the thread to host child windows with a different DPI_AWARENESS_CONTEXT value. This property only effects new windows created within this thread while the mixed hosting behavior is active. A parent window with this hosting behavior is able to host child windows with different DPI_AWARENESS_CONTEXT values, regardless of whether the child windows have mixed hosting behavior enabled.
+ ///
+ /// This hosting behavior does not allow for windows with per-monitor DPI_AWARENESS_CONTEXT values to be hosted until windows with DPI_AWARENESS_CONTEXT values of system or unaware.
+ ///
+ /// To avoid unexpected outcomes, a thread's DPI_HOSTING_BEHAVIOR should be changed to support mixed hosting behaviors only when creating a new window which needs to support those behaviors. Once that window is created, the hosting behavior should be switched back to its default value.
+ ///
+ /// This API is used to change the thread's DPI_HOSTING_BEHAVIOR from its default value. This is only necessary if your app needs to host child windows from plugins and third-party components that do not support per-monitor-aware context. This is most likely to occur if you are updating complex applications to support per-monitor DPI_AWARENESS_CONTEXT behaviors.
+ ///
+ /// Enabling mixed hosting behavior will not automatically adjust the thread's DPI_AWARENESS_CONTEXT to be compatible with legacy content. The thread's awareness context must still be manually changed before new windows are created to host such content.
+ ///
+ /// DpiHostingBehavior
+ /// previous DpiHostingBehavior
+ [DllImport("User32.dll")]
+ private static extern DpiHostingBehavior SetThreadDpiHostingBehavior(DpiHostingBehavior dpiHostingBehavior);
+
+ ///
+ ///Retrieves the DPI_HOSTING_BEHAVIOR from the current thread.
+ ///
+ /// DpiHostingBehavior
+ [DllImport("User32.dll")]
+ private static extern DpiHostingBehavior GetThreadDpiHostingBehavior();
+
+ ///
+ /// Overrides the default per-monitor DPI scaling behavior of a child window in a dialog.
+ /// This function returns TRUE if the operation was successful, and FALSE otherwise. To get extended error information, call GetLastError.
+ ///
+ /// Possible errors are ERROR_INVALID_HANDLE if passed an invalid HWND, and ERROR_ACCESS_DENIED if the windows belongs to another process.
+ ///
+ /// The behaviors are specified as values from the DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS enum. This function follows the typical two-parameter approach to setting flags, where a mask specifies the subset of the flags to be changed.
+ ///
+ /// It is valid to set these behaviors on any window. It does not matter if the window is currently a child of a dialog at the point in time that SetDialogControlDpiChangeBehavior is called. The behaviors are retained and will take effect only when the window is an immediate child of a dialog that has per-monitor DPI scaling enabled.
+ ///
+ /// This API influences individual controls within dialogs. The dialog-wide per-monitor DPI scaling behavior is controlled by SetDialogDpiChangeBehavior.
+ ///
+ /// IntPtr A handle for the window whose behavior will be modified.
+ /// DialogScalingBehaviors A mask specifying the subset of flags to be changed.
+ /// DialogScalingBehaviors The desired value to be set for the specified subset of flags.
+ /// bool
+ [DllImport("User32.dll")]
+ private static extern bool SetDialogControlDpiChangeBehavior(IntPtr hWnd, DialogScalingBehaviors mask, DialogScalingBehaviors values);
+
+ ///
+ /// Retrieves and per-monitor DPI scaling behavior overrides of a child window in a dialog.
+ /// The flags set on the given window. If passed an invalid handle, this function will return zero, and set its last error to ERROR_INVALID_HANDLE.
+ ///
+ /// IntPtr A handle for the window whose behavior will be modified.
+ /// DialogScalingBehaviors
+ [DllImport("User32.dll")]
+ private static extern DialogScalingBehaviors GetDialogControlDpiChangeBehavior(IntPtr hWnd);
+ }
+}
diff --git a/GreenshotPlugin/Core/EffectConverter.cs b/GreenshotPlugin/Core/EffectConverter.cs
new file mode 100644
index 000000000..f72e271dc
--- /dev/null
+++ b/GreenshotPlugin/Core/EffectConverter.cs
@@ -0,0 +1,170 @@
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Globalization;
+using System.Text;
+using GreenshotPlugin.Effects;
+
+namespace GreenshotPlugin.Core
+{
+ public class EffectConverter : TypeConverter {
+ // Fix to prevent BUG-1753
+ private readonly NumberFormatInfo _numberFormatInfo = new NumberFormatInfo();
+
+ public EffectConverter()
+ {
+ _numberFormatInfo.NumberDecimalSeparator = ".";
+ _numberFormatInfo.NumberGroupSeparator = ",";
+ }
+
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
+ if (sourceType == typeof(string)) {
+ return true;
+ }
+ return base.CanConvertFrom(context, sourceType);
+ }
+
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
+ if (destinationType == typeof(string)) {
+ return true;
+ }
+ if (destinationType == typeof(DropShadowEffect)) {
+ return true;
+ }
+ if (destinationType == typeof(TornEdgeEffect)) {
+ return true;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
+ // to string
+ if (destinationType == typeof(string)) {
+ StringBuilder sb = new StringBuilder();
+ if (value.GetType() == typeof(DropShadowEffect)) {
+ DropShadowEffect effect = value as DropShadowEffect;
+ RetrieveDropShadowEffectValues(effect, sb);
+ return sb.ToString();
+ }
+ if (value.GetType() == typeof(TornEdgeEffect)) {
+ TornEdgeEffect effect = value as TornEdgeEffect;
+ RetrieveDropShadowEffectValues(effect, sb);
+ sb.Append("|");
+ RetrieveTornEdgeEffectValues(effect, sb);
+ return sb.ToString();
+ }
+ }
+ // from string
+ if (value is string) {
+ string settings = value as string;
+ if (destinationType == typeof(DropShadowEffect)) {
+ DropShadowEffect effect = new DropShadowEffect();
+ ApplyDropShadowEffectValues(settings, effect);
+ return effect;
+ }
+ if (destinationType == typeof(TornEdgeEffect)) {
+ TornEdgeEffect effect = new TornEdgeEffect();
+ ApplyDropShadowEffectValues(settings, effect);
+ ApplyTornEdgeEffectValues(settings, effect);
+ return effect;
+ }
+ }
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
+ if (value is string settings) {
+ if (settings.Contains("ToothHeight")) {
+ return ConvertTo(context, culture, settings, typeof(TornEdgeEffect));
+ }
+ return ConvertTo(context, culture, settings, typeof(DropShadowEffect));
+ }
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ private void ApplyDropShadowEffectValues(string valuesString, DropShadowEffect effect) {
+ string[] values = valuesString.Split('|');
+ foreach(string nameValuePair in values) {
+ string[] pair = nameValuePair.Split(':');
+ switch (pair[0]) {
+ case "Darkness" :
+ // Fix to prevent BUG-1753
+ if (pair[1] != null && float.TryParse(pair[1], NumberStyles.Float, _numberFormatInfo, out var darkness)) {
+ if (darkness <= 1.0) {
+ effect.Darkness = darkness;
+ }
+ }
+ break;
+ case "ShadowSize":
+ if (int.TryParse(pair[1], out var shadowSize)) {
+ effect.ShadowSize = shadowSize;
+ }
+ break;
+ case "ShadowOffset":
+ Point shadowOffset = new Point();
+ string[] coordinates = pair[1].Split(',');
+ if (int.TryParse(coordinates[0], out var shadowOffsetX)) {
+ shadowOffset.X = shadowOffsetX;
+ }
+ if (int.TryParse(coordinates[1], out var shadowOffsetY)) {
+ shadowOffset.Y = shadowOffsetY;
+ }
+ effect.ShadowOffset = shadowOffset;
+ break;
+ }
+ }
+ }
+
+ private void ApplyTornEdgeEffectValues(string valuesString, TornEdgeEffect effect) {
+ string[] values = valuesString.Split('|');
+ foreach (string nameValuePair in values) {
+ string[] pair = nameValuePair.Split(':');
+ switch (pair[0]) {
+ case "GenerateShadow":
+ if (bool.TryParse(pair[1], out var generateShadow)) {
+ effect.GenerateShadow = generateShadow;
+ }
+ break;
+ case "ToothHeight":
+ if (int.TryParse(pair[1], out var toothHeight)) {
+ effect.ToothHeight = toothHeight;
+ }
+ break;
+ case "HorizontalToothRange":
+ if (int.TryParse(pair[1], out var horizontalToothRange)) {
+ effect.HorizontalToothRange = horizontalToothRange;
+ }
+ break;
+ case "VerticalToothRange":
+ if (int.TryParse(pair[1], out var verticalToothRange)) {
+ effect.VerticalToothRange = verticalToothRange;
+ }
+ break;
+ case "Edges":
+ string[] edges = pair[1].Split(',');
+ if (bool.TryParse(edges[0], out var edge)) {
+ effect.Edges[0] = edge;
+ }
+ if (bool.TryParse(edges[1], out edge)) {
+ effect.Edges[1] = edge;
+ }
+ if (bool.TryParse(edges[2], out edge)) {
+ effect.Edges[2] = edge;
+ }
+ if (bool.TryParse(edges[3], out edge)) {
+ effect.Edges[3] = edge;
+ }
+ break;
+ }
+ }
+ }
+
+ private void RetrieveDropShadowEffectValues(DropShadowEffect effect, StringBuilder sb) {
+ // Fix to prevent BUG-1753 is to use the numberFormatInfo
+ sb.AppendFormat("Darkness:{0}|ShadowSize:{1}|ShadowOffset:{2},{3}", effect.Darkness.ToString("F2", _numberFormatInfo), effect.ShadowSize, effect.ShadowOffset.X, effect.ShadowOffset.Y);
+ }
+ private void RetrieveTornEdgeEffectValues(TornEdgeEffect effect, StringBuilder sb) {
+ sb.AppendFormat("GenerateShadow:{0}|ToothHeight:{1}|HorizontalToothRange:{2}|VerticalToothRange:{3}|Edges:{4},{5},{6},{7}", effect.GenerateShadow, effect.ToothHeight, effect.HorizontalToothRange, effect.VerticalToothRange, effect.Edges[0], effect.Edges[1], effect.Edges[2], effect.Edges[3]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/EmailConfigHelper.cs b/GreenshotPlugin/Core/EmailConfigHelper.cs
new file mode 100644
index 000000000..7da960b09
--- /dev/null
+++ b/GreenshotPlugin/Core/EmailConfigHelper.cs
@@ -0,0 +1,76 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System.IO;
+using Microsoft.Win32;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Description of EmailConfigHelper.
+ ///
+ public static class EmailConfigHelper {
+ private const string OutlookPathKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\OUTLOOK.EXE";
+ private const string MapiClientKey = @"SOFTWARE\Clients\Mail";
+ private const string MapiLocationKey = @"SOFTWARE\Microsoft\Windows Messaging Subsystem";
+ private const string MapiKey = @"MAPI";
+
+ public static string GetMapiClient() {
+ using (RegistryKey key = Registry.CurrentUser.OpenSubKey(MapiClientKey, false)) {
+ if (key != null) {
+ return (string)key.GetValue(string.Empty);
+ }
+ }
+ using (RegistryKey key = Registry.LocalMachine.OpenSubKey(MapiClientKey, false))
+ {
+ return (string) key?.GetValue(string.Empty);
+ }
+ }
+
+ public static bool HasMapi()
+ {
+ using RegistryKey key = Registry.LocalMachine.OpenSubKey(MapiLocationKey, false);
+ return key != null && "1".Equals(key.GetValue(MapiKey, "0"));
+ }
+
+ public static string GetOutlookExePath() {
+ using (RegistryKey key = Registry.LocalMachine.OpenSubKey(OutlookPathKey, false)) {
+ if (key != null) {
+ // "" is the default key, which should point to the outlook location
+ return (string)key.GetValue(string.Empty);
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Check if Outlook is installed
+ ///
+ /// Returns true if outlook is installed
+ public static bool HasOutlook() {
+ string outlookPath = GetOutlookExePath();
+ if (outlookPath != null) {
+ if (File.Exists(outlookPath)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/EnumExtensions.cs b/GreenshotPlugin/Core/EnumExtensions.cs
new file mode 100644
index 000000000..39aff04f1
--- /dev/null
+++ b/GreenshotPlugin/Core/EnumExtensions.cs
@@ -0,0 +1,102 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+namespace GreenshotPlugin.Core {
+ public static class EnumerationExtensions {
+ public static bool Has(this Enum type, T value) {
+ Type underlyingType = Enum.GetUnderlyingType(value.GetType());
+ try {
+ if (underlyingType == typeof(int)) {
+ return (((int)(object)type & (int)(object)value) == (int)(object)value);
+ } else if (underlyingType == typeof(uint)) {
+ return (((uint)(object)type & (uint)(object)value) == (uint)(object)value);
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ return false;
+ }
+
+ public static bool Is(this Enum type, T value) {
+ Type underlyingType = Enum.GetUnderlyingType(value.GetType());
+ try
+ {
+ if (underlyingType == typeof(int)) {
+ return (int)(object)type == (int)(object)value;
+ }
+ if (underlyingType == typeof(uint)) {
+ return (uint)(object)type == (uint)(object)value;
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ return false;
+ }
+
+ ///
+ /// Add a flag to an enum
+ ///
+ ///
+ ///
+ ///
+ public static T Add(this Enum type, T value) {
+ Type underlyingType = Enum.GetUnderlyingType(value.GetType());
+ try
+ {
+ if (underlyingType == typeof(int)) {
+ return (T)(object)(((int)(object)type | (int)(object)value));
+ }
+ if (underlyingType == typeof(uint)) {
+ return (T)(object)(((uint)(object)type | (uint)(object)value));
+ }
+ } catch(Exception ex) {
+ throw new ArgumentException($"Could not append value '{value}' to enumerated type '{typeof(T).Name}'.", ex);
+ }
+ throw new ArgumentException($"Could not append value '{value}' to enumerated type '{typeof(T).Name}'.");
+ }
+
+ ///
+ /// Remove a flag from an enum type
+ ///
+ ///
+ ///
+ ///
+ public static T Remove(this Enum type, T value) {
+ Type underlyingType = Enum.GetUnderlyingType(value.GetType());
+ try
+ {
+ if (underlyingType == typeof(int)) {
+ return (T)(object)(((int)(object)type & ~(int)(object)value));
+ }
+ if (underlyingType == typeof(uint)) {
+ return (T)(object)(((uint)(object)type & ~(uint)(object)value));
+ }
+ } catch(Exception ex) {
+ throw new ArgumentException($"Could not remove value '{value}' from enumerated type '{typeof(T).Name}'.", ex);
+ }
+ throw new ArgumentException($"Could not remove value '{value}' from enumerated type '{typeof(T).Name}'.");
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/Enums/DialogDpiChangeBehaviors.cs b/GreenshotPlugin/Core/Enums/DialogDpiChangeBehaviors.cs
new file mode 100644
index 000000000..fa5b61635
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/DialogDpiChangeBehaviors.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// In Per Monitor v2 contexts, dialogs will automatically respond to DPI changes by resizing themselves and re-computing the positions of their child windows (here referred to as re-layouting). This enum works in conjunction with SetDialogDpiChangeBehavior in order to override the default DPI scaling behavior for dialogs.
+ /// This does not affect DPI scaling behavior for the child windows of dialogs(beyond re-layouting), which is controlled by DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS.
+ ///
+ [Flags]
+ public enum DialogDpiChangeBehaviors
+ {
+ ///
+ /// The default behavior of the dialog manager. In response to a DPI change, the dialog manager will re-layout each control, update the font on each control, resize the dialog, and update the dialog's own font.
+ ///
+ Default = 0,
+
+ ///
+ /// Prevents the dialog manager from responding to WM_GETDPISCALEDSIZE and WM_DPICHANGED, disabling all default DPI scaling behavior.
+ ///
+ DisableAll = 1,
+
+ ///
+ /// Prevents the dialog manager from resizing the dialog in response to a DPI change.
+ ///
+ DisableResize = 2,
+
+ ///
+ /// Prevents the dialog manager from re-layouting all of the dialogue's immediate children HWNDs in response to a DPI change.
+ ///
+ DisableControlRelayout = 3
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/Enums/DialogScalingBehaviors.cs b/GreenshotPlugin/Core/Enums/DialogScalingBehaviors.cs
new file mode 100644
index 000000000..4de8e4c35
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/DialogScalingBehaviors.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// Describes per-monitor DPI scaling behavior overrides for child windows within dialogs. The values in this enumeration are bitfields and can be combined.
+ ///
+ /// This enum is used with SetDialogControlDpiChangeBehavior in order to override the default per-monitor DPI scaling behavior for a child window within a dialog.
+ ///
+ /// These settings only apply to individual controls within dialogs. The dialog-wide per-monitor DPI scaling behavior of a dialog is controlled by DIALOG_DPI_CHANGE_BEHAVIORS.
+ ///
+ [Flags]
+ public enum DialogScalingBehaviors
+ {
+ ///
+ /// The default behavior of the dialog manager. The dialog managed will update the font, size, and position of the child window on DPI changes.
+ ///
+ Default = 0,
+
+ ///
+ /// Prevents the dialog manager from sending an updated font to the child window via WM_SETFONT in response to a DPI change.
+ ///
+ DisableFontUpdate = 1,
+
+ ///
+ /// Prevents the dialog manager from resizing and repositioning the child window in response to a DPI change.
+ ///
+ DisableRelayout = 2
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/Enums/DpiAwareness.cs b/GreenshotPlugin/Core/Enums/DpiAwareness.cs
new file mode 100644
index 000000000..7dd22431e
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/DpiAwareness.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// Identifies the dots per inch (dpi) setting for a thread, process, or window.
+ /// Can be used everywhere ProcessDpiAwareness is passed.
+ ///
+ public enum DpiAwareness
+ {
+ ///
+ /// Invalid DPI awareness. This is an invalid DPI awareness value.
+ ///
+ Invalid = -1,
+
+ ///
+ /// DPI unaware.
+ /// This process does not scale for DPI changes and is always assumed to have a scale factor of 100% (96 DPI).
+ /// It will be automatically scaled by the system on any other DPI setting.
+ ///
+ Unaware = 0,
+
+ ///
+ /// System DPI aware.
+ /// This process does not scale for DPI changes.
+ /// It will query for the DPI once and use that value for the lifetime of the process.
+ /// If the DPI changes, the process will not adjust to the new DPI value.
+ /// It will be automatically scaled up or down by the system when the DPI changes from the system value.
+ ///
+ SystemAware = 1,
+
+ ///
+ /// Per monitor DPI aware.
+ /// This process checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes.
+ /// These processes are not automatically scaled by the system.
+ ///
+ PerMonitorAware = 2
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/Enums/DpiAwarenessContext.cs b/GreenshotPlugin/Core/Enums/DpiAwarenessContext.cs
new file mode 100644
index 000000000..276d73550
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/DpiAwarenessContext.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ ///
+ public enum DpiAwarenessContext
+ {
+ ///
+ /// DPI unaware.
+ /// This window does not scale for DPI changes and is always assumed to have a scale factor of 100% (96 DPI).
+ /// It will be automatically scaled by the system on any other DPI setting.
+ ///
+ Unaware = -1,
+
+ ///
+ /// System DPI aware.
+ /// This window does not scale for DPI changes.
+ /// It will query for the DPI once and use that value for the lifetime of the process.
+ /// If the DPI changes, the process will not adjust to the new DPI value.
+ /// It will be automatically scaled up or down by the system when the DPI changes from the system value.
+ ///
+ SystemAware = -2,
+
+ ///
+ /// Per monitor DPI aware.
+ /// This window checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes.
+ /// These processes are not automatically scaled by the system.
+ ///
+ PerMonitorAware = -3,
+
+ ///
+ /// Also known as Per Monitor v2. An advancement over the original per-monitor DPI awareness mode, which enables applications to access new DPI-related scaling behaviors on a per top-level window basis.
+ /// Per Monitor v2 was made available in the Creators Update of Windows 10, and is not available on earlier versions of the operating system.
+ /// The additional behaviors introduced are as follows:
+ /// * Child window DPI change notifications - In Per Monitor v2 contexts, the entire window tree is notified of any DPI changes that occur.
+ /// * Scaling of non-client area - All windows will automatically have their non-client area drawn in a DPI sensitive fashion. Calls to EnableNonClientDpiScaling are unnecessary.
+ /// * Scaling of Win32 menus - All NTUSER menus created in Per Monitor v2 contexts will be scaling in a per-monitor fashion.
+ /// * Dialog Scaling - Win32 dialogs created in Per Monitor v2 contexts will automatically respond to DPI changes.
+ /// * Improved scaling of comctl32 controls - Various comctl32 controls have improved DPI scaling behavior in Per Monitor v2 contexts.
+ /// * Improved theming behavior - UxTheme handles opened in the context of a Per Monitor v2 window will operate in terms of the DPI associated with that window.
+ ///
+ PerMonitorAwareV2 = -4
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/Enums/DpiHostingBehavior.cs b/GreenshotPlugin/Core/Enums/DpiHostingBehavior.cs
new file mode 100644
index 000000000..0b0abf686
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/DpiHostingBehavior.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// Identifies the DPI hosting behavior for a window.
+ /// This behavior allows windows created in the thread to host child windows with a different DPI_AWARENESS_CONTEXT
+ ///
+ public enum DpiHostingBehavior
+ {
+ ///
+ /// Invalid DPI hosting behavior. This usually occurs if the previous SetThreadDpiHostingBehavior call used an invalid parameter.
+ ///
+ Invalid = -1,
+
+ ///
+ /// Default DPI hosting behavior. The associated window behaves as normal, and cannot create or re-parent child windows with a different DPI_AWARENESS_CONTEXT.
+ ///
+ Default = 0,
+
+ ///
+ /// Mixed DPI hosting behavior. This enables the creation and re-parenting of child windows with different DPI_AWARENESS_CONTEXT. These child windows will be independently scaled by the OS.
+ ///
+ Mixed = 1
+
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/Enums/HResult.cs b/GreenshotPlugin/Core/Enums/HResult.cs
new file mode 100644
index 000000000..55f61aaf0
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/HResult.cs
@@ -0,0 +1,56 @@
+// Greenshot - a free and open source screenshot tool
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+//
+// For more information see: http://getgreenshot.org/
+// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 1 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// The HRESULT represents Windows error codes
+ /// See wikipedia
+ ///
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum HResult
+ {
+#pragma warning disable 1591
+ S_OK = 0,
+ S_FALSE = 1,
+ E_FAIL = unchecked((int)0x80004005),
+ E_INVALIDARG = unchecked((int)0x80070057),
+ E_NOTIMPL = unchecked((int)0x80004001),
+ E_POINTER = unchecked((int)0x80004003),
+ E_PENDING = unchecked((int)0x8000000A),
+ E_NOINTERFACE = unchecked((int)0x80004002),
+ E_ABORT = unchecked((int)0x80004004),
+ E_ACCESSDENIED = unchecked((int)0x80070006),
+ E_HANDLE = unchecked((int)0x80070006),
+ E_UNEXPECTED = unchecked((int)0x8000FFFF),
+ E_FILENOTFOUND = unchecked((int)0x80070002),
+ E_PATHNOTFOUND = unchecked((int)0x80070003),
+ E_INVALID_DATA = unchecked((int)0x8007000D),
+ E_OUTOFMEMORY = unchecked((int)0x8007000E),
+ E_INSUFFICIENT_BUFFER = unchecked((int)0x8007007A),
+ WSAECONNABORTED = unchecked((int)0x80072745),
+ WSAECONNRESET = unchecked((int)0x80072746),
+ ERROR_TOO_MANY_CMDS = unchecked((int)0x80070038),
+ ERROR_NOT_SUPPORTED = unchecked((int)0x80070032),
+ TYPE_E_ELEMENTNOTFOUND = unchecked((int)0x8002802B)
+#pragma warning restore 1591
+ }
+}
diff --git a/GreenshotPlugin/Core/Enums/MonitorDpiType.cs b/GreenshotPlugin/Core/Enums/MonitorDpiType.cs
new file mode 100644
index 000000000..ebe0033b4
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/MonitorDpiType.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// See
+ ///
+ /// MONITOR_DPI_TYPE
+ /// enumeration
+ ///
+ ///
+ [Flags]
+ public enum MonitorDpiType
+ {
+ ///
+ /// The effective DPI.
+ /// This value should be used when determining the correct scale factor for scaling UI elements.
+ /// This incorporates the scale factor set by the user for this specific display.
+ ///
+ EffectiveDpi = 0,
+
+ ///
+ /// The angular DPI.
+ /// This DPI ensures rendering at a compliant angular resolution on the screen.
+ /// This does not include the scale factor set by the user for this specific display
+ ///
+ AngularDpi = 1,
+
+ ///
+ /// The raw DPI.
+ /// This value is the linear DPI of the screen as measured on the screen itself.
+ /// Use this value when you want to read the pixel density and not the recommended scaling setting.
+ /// This does not include the scale factor set by the user for this specific display and is not guaranteed to be a
+ /// supported DPI value.
+ ///
+ RawDpi = 2
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/Enums/MonitorFrom.cs b/GreenshotPlugin/Core/Enums/MonitorFrom.cs
new file mode 100644
index 000000000..57e57a390
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/MonitorFrom.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// Flags for the MonitorFromRect / MonitorFromWindow "flags" field
+ /// see MonitorFromRect function
+ /// or see MonitorFromWindow function
+ ///
+ [Flags]
+ public enum MonitorFrom : uint
+ {
+ ///
+ /// Returns a handle to the display monitor that is nearest to the rectangle.
+ ///
+ DefaultToNearest = 0,
+
+ ///
+ /// Returns NULL. (why??)
+ ///
+ DefaultToNull = 1,
+
+ ///
+ /// Returns a handle to the primary display monitor.
+ ///
+ DefaultToPrimary = 2
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/Enums/SystemParametersInfoActions.cs b/GreenshotPlugin/Core/Enums/SystemParametersInfoActions.cs
new file mode 100644
index 000000000..62a4eb5f4
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/SystemParametersInfoActions.cs
@@ -0,0 +1,1390 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Diagnostics.CodeAnalysis;
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// SPI_ System-wide parameter - Used in SystemParametersInfo function
+ ///
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum SystemParametersInfoActions : uint
+ {
+ ///
+ /// No value
+ ///
+ SPI_NONE = 0,
+
+ ///
+ /// Determines whether the warning beeper is on.
+ /// The pvParam parameter must point to a BOOL variable that receives TRUE if the beeper is on, or FALSE if it is off.
+ ///
+ SPI_GETBEEP = 0x0001,
+
+ ///
+ /// Turns the warning beeper on or off. The uiParam parameter specifies TRUE for on, or FALSE for off.
+ ///
+ SPI_SETBEEP = 0x0002,
+
+ ///
+ /// Retrieves the two mouse threshold values and the mouse speed.
+ ///
+ SPI_GETMOUSE = 0x0003,
+
+ ///
+ /// Sets the two mouse threshold values and the mouse speed.
+ ///
+ SPI_SETMOUSE = 0x0004,
+
+ ///
+ /// Retrieves the border multiplier factor that determines the width of a window's sizing border.
+ /// The pvParam parameter must point to an integer variable that receives this value.
+ ///
+ SPI_GETBORDER = 0x0005,
+
+ ///
+ /// Sets the border multiplier factor that determines the width of a window's sizing border.
+ /// The uiParam parameter specifies the new value.
+ ///
+ SPI_SETBORDER = 0x0006,
+
+ ///
+ /// Retrieves the keyboard repeat-speed setting, which is a value in the range from 0 (approximately 2.5 repetitions
+ /// per second)
+ /// through 31 (approximately 30 repetitions per second). The actual repeat rates are hardware-dependent and may vary
+ /// from
+ /// a linear scale by as much as 20%. The pvParam parameter must point to a DWORD variable that receives the setting
+ ///
+ SPI_GETKEYBOARDSPEED = 0x000A,
+
+ ///
+ /// Sets the keyboard repeat-speed setting. The uiParam parameter must specify a value in the range from 0
+ /// (approximately 2.5 repetitions per second) through 31 (approximately 30 repetitions per second).
+ /// The actual repeat rates are hardware-dependent and may vary from a linear scale by as much as 20%.
+ /// If uiParam is greater than 31, the parameter is set to 31.
+ ///
+ SPI_SETKEYBOARDSPEED = 0x000B,
+
+ ///
+ /// Not implemented.
+ ///
+ SPI_LANGDRIVER = 0x000C,
+
+ ///
+ /// Sets or retrieves the width, in pixels, of an icon cell. The system uses this rectangle to arrange icons in large
+ /// icon view.
+ /// To set this value, set uiParam to the new value and set pvParam to null. You cannot set this value to less than
+ /// SM_CXICON.
+ /// To retrieve this value, pvParam must point to an integer that receives the current value.
+ ///
+ SPI_ICONHORIZONTALSPACING = 0x000D,
+
+ ///
+ /// Retrieves the screen saver time-out value, in seconds. The pvParam parameter must point to an integer variable that
+ /// receives the value.
+ ///
+ SPI_GETSCREENSAVETIMEOUT = 0x000E,
+
+ ///
+ /// Sets the screen saver time-out value to the value of the uiParam parameter. This value is the amount of time, in
+ /// seconds,
+ /// that the system must be idle before the screen saver activates.
+ ///
+ SPI_SETSCREENSAVETIMEOUT = 0x000F,
+
+ ///
+ /// Determines whether screen saving is enabled. The pvParam parameter must point to a bool variable that receives TRUE
+ /// if screen saving is enabled, or FALSE otherwise.
+ /// Does not work for Windows 7: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx
+ ///
+ SPI_GETSCREENSAVEACTIVE = 0x0010,
+
+ ///
+ /// Sets the state of the screen saver. The uiParam parameter specifies TRUE to activate screen saving, or FALSE to
+ /// deactivate it.
+ ///
+ SPI_SETSCREENSAVEACTIVE = 0x0011,
+
+ ///
+ /// Retrieves the current granularity value of the desktop sizing grid. The pvParam parameter must point to an integer
+ /// variable
+ /// that receives the granularity.
+ ///
+ SPI_GETGRIDGRANULARITY = 0x0012,
+
+ ///
+ /// Sets the granularity of the desktop sizing grid to the value of the uiParam parameter.
+ ///
+ SPI_SETGRIDGRANULARITY = 0x0013,
+
+ ///
+ /// Sets the desktop wallpaper. The value of the pvParam parameter determines the new wallpaper. To specify a wallpaper
+ /// bitmap,
+ /// set pvParam to point to a null-terminated string containing the name of a bitmap file. Setting pvParam to ""
+ /// removes the wallpaper.
+ /// Setting pvParam to SETWALLPAPER_DEFAULT or null reverts to the default wallpaper.
+ ///
+ SPI_SETDESKWALLPAPER = 0x0014,
+
+ ///
+ /// Sets the current desktop pattern by causing Windows to read the Pattern= setting from the WIN.INI file.
+ ///
+ SPI_SETDESKPATTERN = 0x0015,
+
+ ///
+ /// Retrieves the keyboard repeat-delay setting, which is a value in the range from 0 (approximately 250 ms delay)
+ /// through 3
+ /// (approximately 1 second delay). The actual delay associated with each value may vary depending on the hardware. The
+ /// pvParam parameter must point to an integer variable that receives the setting.
+ ///
+ SPI_GETKEYBOARDDELAY = 0x0016,
+
+ ///
+ /// Sets the keyboard repeat-delay setting. The uiParam parameter must specify 0, 1, 2, or 3, where zero sets the
+ /// shortest delay
+ /// (approximately 250 ms) and 3 sets the longest delay (approximately 1 second). The actual delay associated with each
+ /// value may
+ /// vary depending on the hardware.
+ ///
+ SPI_SETKEYBOARDDELAY = 0x0017,
+
+ ///
+ /// Sets or retrieves the height, in pixels, of an icon cell.
+ /// To set this value, set uiParam to the new value and set pvParam to null. You cannot set this value to less than
+ /// SM_CYICON.
+ /// To retrieve this value, pvParam must point to an integer that receives the current value.
+ ///
+ SPI_ICONVERTICALSPACING = 0x0018,
+
+ ///
+ /// Determines whether icon-title wrapping is enabled. The pvParam parameter must point to a bool variable that
+ /// receives TRUE
+ /// if enabled, or FALSE otherwise.
+ ///
+ SPI_GETICONTITLEWRAP = 0x0019,
+
+ ///
+ /// Turns icon-title wrapping on or off. The uiParam parameter specifies TRUE for on, or FALSE for off.
+ ///
+ SPI_SETICONTITLEWRAP = 0x001A,
+
+ ///
+ /// Determines whether pop-up menus are left-aligned or right-aligned, relative to the corresponding menu-bar item.
+ /// The pvParam parameter must point to a bool variable that receives TRUE if left-aligned, or FALSE otherwise.
+ ///
+ SPI_GETMENUDROPALIGNMENT = 0x001B,
+
+ ///
+ /// Sets the alignment value of pop-up menus. The uiParam parameter specifies TRUE for right alignment, or FALSE for
+ /// left alignment.
+ ///
+ SPI_SETMENUDROPALIGNMENT = 0x001C,
+
+ ///
+ /// Sets the width of the double-click rectangle to the value of the uiParam parameter.
+ /// The double-click rectangle is the rectangle within which the second click of a double-click must fall for it to be
+ /// registered
+ /// as a double-click.
+ /// To retrieve the width of the double-click rectangle, call GetSystemMetrics with the SM_CXDOUBLECLK flag.
+ ///
+ SPI_SETDOUBLECLKWIDTH = 0x001D,
+
+ ///
+ /// Sets the height of the double-click rectangle to the value of the uiParam parameter.
+ /// The double-click rectangle is the rectangle within which the second click of a double-click must fall for it to be
+ /// registered
+ /// as a double-click.
+ /// To retrieve the height of the double-click rectangle, call GetSystemMetrics with the SM_CYDOUBLECLK flag.
+ ///
+ SPI_SETDOUBLECLKHEIGHT = 0x001E,
+
+ ///
+ /// Retrieves the logical font information for the current icon-title font. The uiParam parameter specifies the size of
+ /// a LOGFONT structure,
+ /// and the pvParam parameter must point to the LOGFONT structure to fill in.
+ ///
+ SPI_GETICONTITLELOGFONT = 0x001F,
+
+ ///
+ /// Sets the double-click time for the mouse to the value of the uiParam parameter. The double-click time is the
+ /// maximum number
+ /// of milliseconds that can occur between the first and second clicks of a double-click. You can also call the
+ /// SetDoubleClickTime
+ /// function to set the double-click time. To get the current double-click time, call the GetDoubleClickTime function.
+ ///
+ SPI_SETDOUBLECLICKTIME = 0x0020,
+
+ ///
+ /// Swaps or restores the meaning of the left and right mouse buttons. The uiParam parameter specifies TRUE to swap the
+ /// meanings
+ /// of the buttons, or FALSE to restore their original meanings.
+ ///
+ SPI_SETMOUSEBUTTONSWAP = 0x0021,
+
+ ///
+ /// Sets the font that is used for icon titles. The uiParam parameter specifies the size of a LOGFONT structure,
+ /// and the pvParam parameter must point to a LOGFONT structure.
+ ///
+ SPI_SETICONTITLELOGFONT = 0x0022,
+
+ ///
+ /// This flag is obsolete. Previous versions of the system use this flag to determine whether ALT+TAB fast task
+ /// switching is enabled.
+ /// For Windows 95, Windows 98, and Windows NT version 4.0 and later, fast task switching is always enabled.
+ ///
+ SPI_GETFASTTASKSWITCH = 0x0023,
+
+ ///
+ /// This flag is obsolete. Previous versions of the system use this flag to enable or disable ALT+TAB fast task
+ /// switching.
+ /// For Windows 95, Windows 98, and Windows NT version 4.0 and later, fast task switching is always enabled.
+ ///
+ SPI_SETFASTTASKSWITCH = 0x0024,
+
+ ///
+ /// Sets dragging of full windows either on or off. The uiParam parameter specifies TRUE for on, or FALSE for off.
+ /// Windows 95: This flag is supported only if Windows Plus! is installed. See SPI_GETWINDOWSEXTENSION.
+ ///
+ SPI_SETDRAGFULLWINDOWS = 0x0025,
+
+ ///
+ /// Determines whether dragging of full windows is enabled. The pvParam parameter must point to a BOOL variable that
+ /// receives TRUE
+ /// if enabled, or FALSE otherwise.
+ /// Windows 95: This flag is supported only if Windows Plus! is installed. See SPI_GETWINDOWSEXTENSION.
+ ///
+ SPI_GETDRAGFULLWINDOWS = 0x0026,
+
+ ///
+ /// Retrieves the metrics associated with the nonclient area of nonminimized windows. The pvParam parameter must point
+ /// to a NONCLIENTMETRICS structure that receives the information. Set the cbSize member of this structure and the
+ /// uiParam parameter
+ /// to sizeof(NONCLIENTMETRICS).
+ ///
+ SPI_GETNONCLIENTMETRICS = 0x0029,
+
+ ///
+ /// Sets the metrics associated with the nonclient area of nonminimized windows. The pvParam parameter must point
+ /// to a NONCLIENTMETRICS structure that contains the new parameters. Set the cbSize member of this structure
+ /// and the uiParam parameter to sizeof(NONCLIENTMETRICS). Also, the lfHeight member of the LOGFONT structure must be a
+ /// negative value.
+ ///
+ SPI_SETNONCLIENTMETRICS = 0x002A,
+
+ ///
+ /// Retrieves the metrics associated with minimized windows. The pvParam parameter must point to a MINIMIZEDMETRICS
+ /// structure
+ /// that receives the information. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(MINIMIZEDMETRICS).
+ ///
+ SPI_GETMINIMIZEDMETRICS = 0x002B,
+
+ ///
+ /// Sets the metrics associated with minimized windows. The pvParam parameter must point to a MINIMIZEDMETRICS
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(MINIMIZEDMETRICS).
+ ///
+ SPI_SETMINIMIZEDMETRICS = 0x002C,
+
+ ///
+ /// Retrieves the metrics associated with icons. The pvParam parameter must point to an ICONMETRICS structure that
+ /// receives
+ /// the information. Set the cbSize member of this structure and the uiParam parameter to sizeof(ICONMETRICS).
+ ///
+ SPI_GETICONMETRICS = 0x002D,
+
+ ///
+ /// Sets the metrics associated with icons. The pvParam parameter must point to an ICONMETRICS structure that contains
+ /// the new parameters. Set the cbSize member of this structure and the uiParam parameter to sizeof(ICONMETRICS).
+ ///
+ SPI_SETICONMETRICS = 0x002E,
+
+ ///
+ /// Sets the size of the work area. The work area is the portion of the screen not obscured by the system taskbar
+ /// or by application desktop toolbars. The pvParam parameter is a pointer to a RECT structure that specifies the new
+ /// work area rectangle,
+ /// expressed in virtual screen coordinates. In a system with multiple display monitors, the function sets the work
+ /// area
+ /// of the monitor that contains the specified rectangle.
+ ///
+ SPI_SETWORKAREA = 0x002F,
+
+ ///
+ /// Retrieves the size of the work area on the primary display monitor. The work area is the portion of the screen not
+ /// obscured
+ /// by the system taskbar or by application desktop toolbars. The pvParam parameter must point to a RECT structure that
+ /// receives
+ /// the coordinates of the work area, expressed in virtual screen coordinates.
+ /// To get the work area of a monitor other than the primary display monitor, call the GetMonitorInfo function.
+ ///
+ SPI_GETWORKAREA = 0x0030,
+
+ ///
+ /// Windows Me/98/95: Pen windows is being loaded or unloaded. The uiParam parameter is TRUE when loading and FALSE
+ /// when unloading pen windows. The pvParam parameter is null.
+ ///
+ SPI_SETPENWINDOWS = 0x0031,
+
+ ///
+ /// Retrieves information about the HighContrast accessibility feature. The pvParam parameter must point to a
+ /// HIGHCONTRAST structure
+ /// that receives the information. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(HIGHCONTRAST).
+ /// For a general discussion, see remarks.
+ /// Windows NT: This value is not supported.
+ ///
+ ///
+ /// There is a difference between the High Contrast color scheme and the High Contrast Mode. The High Contrast color
+ /// scheme changes
+ /// the system colors to colors that have obvious contrast; you switch to this color scheme by using the Display
+ /// Options in the control panel.
+ /// The High Contrast Mode, which uses SPI_GETHIGHCONTRAST and SPI_SETHIGHCONTRAST, advises applications to modify
+ /// their appearance
+ /// for visually-impaired users. It involves such things as audible warning to users and customized color scheme
+ /// (using the Accessibility Options in the control panel). For more information, see HIGHCONTRAST on MSDN.
+ /// For more information on general accessibility features, see Accessibility on MSDN.
+ ///
+ SPI_GETHIGHCONTRAST = 0x0042,
+
+ ///
+ /// Sets the parameters of the HighContrast accessibility feature. The pvParam parameter must point to a HIGHCONTRAST
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(HIGHCONTRAST).
+ /// Windows NT: This value is not supported.
+ ///
+ SPI_SETHIGHCONTRAST = 0x0043,
+
+ ///
+ /// Determines whether the user relies on the keyboard instead of the mouse, and wants applications to display keyboard
+ /// interfaces
+ /// that would otherwise be hidden. The pvParam parameter must point to a BOOL variable that receives TRUE
+ /// if the user relies on the keyboard; or FALSE otherwise.
+ /// Windows NT: This value is not supported.
+ ///
+ SPI_GETKEYBOARDPREF = 0x0044,
+
+ ///
+ /// Sets the keyboard preference. The uiParam parameter specifies TRUE if the user relies on the keyboard instead of
+ /// the mouse,
+ /// and wants applications to display keyboard interfaces that would otherwise be hidden; uiParam is FALSE otherwise.
+ /// Windows NT: This value is not supported.
+ ///
+ SPI_SETKEYBOARDPREF = 0x0045,
+
+ ///
+ /// Determines whether a screen reviewer utility is running. A screen reviewer utility directs textual information to
+ /// an output device,
+ /// such as a speech synthesizer or Braille display. When this flag is set, an application should provide textual
+ /// information
+ /// in situations where it would otherwise present the information graphically.
+ /// The pvParam parameter is a pointer to a BOOL variable that receives TRUE if a screen reviewer utility is running,
+ /// or FALSE otherwise.
+ /// Windows NT: This value is not supported.
+ ///
+ SPI_GETSCREENREADER = 0x0046,
+
+ ///
+ /// Determines whether a screen review utility is running. The uiParam parameter specifies TRUE for on, or FALSE for
+ /// off.
+ /// Windows NT: This value is not supported.
+ ///
+ SPI_SETSCREENREADER = 0x0047,
+
+ ///
+ /// Retrieves the animation effects associated with user actions. The pvParam parameter must point to an ANIMATIONINFO
+ /// structure
+ /// that receives the information. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(ANIMATIONINFO).
+ ///
+ SPI_GETANIMATION = 0x0048,
+
+ ///
+ /// Sets the animation effects associated with user actions. The pvParam parameter must point to an ANIMATIONINFO
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(ANIMATIONINFO).
+ ///
+ SPI_SETANIMATION = 0x0049,
+
+ ///
+ /// Determines whether the font smoothing feature is enabled. This feature uses font antialiasing to make font curves
+ /// appear smoother
+ /// by painting pixels at different gray levels.
+ /// The pvParam parameter must point to a BOOL variable that receives TRUE if the feature is enabled, or FALSE if it is
+ /// not.
+ /// Windows 95: This flag is supported only if Windows Plus! is installed. See SPI_GETWINDOWSEXTENSION.
+ ///
+ SPI_GETFONTSMOOTHING = 0x004A,
+
+ ///
+ /// Enables or disables the font smoothing feature, which uses font antialiasing to make font curves appear smoother
+ /// by painting pixels at different gray levels.
+ /// To enable the feature, set the uiParam parameter to TRUE. To disable the feature, set uiParam to FALSE.
+ /// Windows 95: This flag is supported only if Windows Plus! is installed. See SPI_GETWINDOWSEXTENSION.
+ ///
+ SPI_SETFONTSMOOTHING = 0x004B,
+
+ ///
+ /// Sets the width, in pixels, of the rectangle used to detect the start of a drag operation. Set uiParam to the new
+ /// value.
+ /// To retrieve the drag width, call GetSystemMetrics with the SM_CXDRAG flag.
+ ///
+ SPI_SETDRAGWIDTH = 0x004C,
+
+ ///
+ /// Sets the height, in pixels, of the rectangle used to detect the start of a drag operation. Set uiParam to the new
+ /// value.
+ /// To retrieve the drag height, call GetSystemMetrics with the SM_CYDRAG flag.
+ ///
+ SPI_SETDRAGHEIGHT = 0x004D,
+
+ ///
+ /// Used internally; applications should not use this value.
+ ///
+ SPI_SETHANDHELD = 0x004E,
+
+ ///
+ /// Retrieves the time-out value for the low-power phase of screen saving. The pvParam parameter must point to an
+ /// integer variable
+ /// that receives the value. This flag is supported for 32-bit applications only.
+ /// Windows NT, Windows Me/98: This flag is supported for 16-bit and 32-bit applications.
+ /// Windows 95: This flag is supported for 16-bit applications only.
+ ///
+ SPI_GETLOWPOWERTIMEOUT = 0x004F,
+
+ ///
+ /// Retrieves the time-out value for the power-off phase of screen saving. The pvParam parameter must point to an
+ /// integer variable
+ /// that receives the value. This flag is supported for 32-bit applications only.
+ /// Windows NT, Windows Me/98: This flag is supported for 16-bit and 32-bit applications.
+ /// Windows 95: This flag is supported for 16-bit applications only.
+ ///
+ SPI_GETPOWEROFFTIMEOUT = 0x0050,
+
+ ///
+ /// Sets the time-out value, in seconds, for the low-power phase of screen saving. The uiParam parameter specifies the
+ /// new value.
+ /// The pvParam parameter must be null. This flag is supported for 32-bit applications only.
+ /// Windows NT, Windows Me/98: This flag is supported for 16-bit and 32-bit applications.
+ /// Windows 95: This flag is supported for 16-bit applications only.
+ ///
+ SPI_SETLOWPOWERTIMEOUT = 0x0051,
+
+ ///
+ /// Sets the time-out value, in seconds, for the power-off phase of screen saving. The uiParam parameter specifies the
+ /// new value.
+ /// The pvParam parameter must be null. This flag is supported for 32-bit applications only.
+ /// Windows NT, Windows Me/98: This flag is supported for 16-bit and 32-bit applications.
+ /// Windows 95: This flag is supported for 16-bit applications only.
+ ///
+ SPI_SETPOWEROFFTIMEOUT = 0x0052,
+
+ ///
+ /// Determines whether the low-power phase of screen saving is enabled. The pvParam parameter must point to a BOOL
+ /// variable
+ /// that receives TRUE if enabled, or FALSE if disabled. This flag is supported for 32-bit applications only.
+ /// Windows NT, Windows Me/98: This flag is supported for 16-bit and 32-bit applications.
+ /// Windows 95: This flag is supported for 16-bit applications only.
+ ///
+ SPI_GETLOWPOWERACTIVE = 0x0053,
+
+ ///
+ /// Determines whether the power-off phase of screen saving is enabled. The pvParam parameter must point to a BOOL
+ /// variable
+ /// that receives TRUE if enabled, or FALSE if disabled. This flag is supported for 32-bit applications only.
+ /// Windows NT, Windows Me/98: This flag is supported for 16-bit and 32-bit applications.
+ /// Windows 95: This flag is supported for 16-bit applications only.
+ ///
+ SPI_GETPOWEROFFACTIVE = 0x0054,
+
+ ///
+ /// Activates or deactivates the low-power phase of screen saving. Set uiParam to 1 to activate, or zero to deactivate.
+ /// The pvParam parameter must be null. This flag is supported for 32-bit applications only.
+ /// Windows NT, Windows Me/98: This flag is supported for 16-bit and 32-bit applications.
+ /// Windows 95: This flag is supported for 16-bit applications only.
+ ///
+ SPI_SETLOWPOWERACTIVE = 0x0055,
+
+ ///
+ /// Activates or deactivates the power-off phase of screen saving. Set uiParam to 1 to activate, or zero to deactivate.
+ /// The pvParam parameter must be null. This flag is supported for 32-bit applications only.
+ /// Windows NT, Windows Me/98: This flag is supported for 16-bit and 32-bit applications.
+ /// Windows 95: This flag is supported for 16-bit applications only.
+ ///
+ SPI_SETPOWEROFFACTIVE = 0x0056,
+
+ ///
+ /// Reloads the system cursors. Set the uiParam parameter to zero and the pvParam parameter to null.
+ ///
+ SPI_SETCURSORS = 0x0057,
+
+ ///
+ /// Reloads the system icons. Set the uiParam parameter to zero and the pvParam parameter to null.
+ ///
+ SPI_SETICONS = 0x0058,
+
+ ///
+ /// Retrieves the input locale identifier for the system default input language. The pvParam parameter must point
+ /// to an HKL variable that receives this value. For more information, see Languages, Locales, and Keyboard Layouts on
+ /// MSDN.
+ ///
+ SPI_GETDEFAULTINPUTLANG = 0x0059,
+
+ ///
+ /// Sets the default input language for the system shell and applications. The specified language must be displayable
+ /// using the current system character set. The pvParam parameter must point to an HKL variable that contains
+ /// the input locale identifier for the default language. For more information, see Languages, Locales, and Keyboard
+ /// Layouts on MSDN.
+ ///
+ SPI_SETDEFAULTINPUTLANG = 0x005A,
+
+ ///
+ /// Sets the hot key set for switching between input languages. The uiParam and pvParam parameters are not used.
+ /// The value sets the shortcut keys in the keyboard property sheets by reading the registry again. The registry must
+ /// be set before this flag is used. the path in the registry is \HKEY_CURRENT_USER\keyboard layout\toggle. Valid
+ /// values are "1" = ALT+SHIFT, "2" = CTRL+SHIFT, and "3" = none.
+ ///
+ SPI_SETLANGTOGGLE = 0x005B,
+
+ ///
+ /// Windows 95: Determines whether the Windows extension, Windows Plus!, is installed. Set the uiParam parameter to 1.
+ /// The pvParam parameter is not used. The function returns TRUE if the extension is installed, or FALSE if it is not.
+ ///
+ SPI_GETWINDOWSEXTENSION = 0x005C,
+
+ ///
+ /// Enables or disables the Mouse Trails feature, which improves the visibility of mouse cursor movements by briefly
+ /// showing
+ /// a trail of cursors and quickly erasing them.
+ /// To disable the feature, set the uiParam parameter to zero or 1. To enable the feature, set uiParam to a value
+ /// greater than 1
+ /// to indicate the number of cursors drawn in the trail.
+ /// Windows 2000/NT: This value is not supported.
+ ///
+ SPI_SETMOUSETRAILS = 0x005D,
+
+ ///
+ /// Determines whether the Mouse Trails feature is enabled. This feature improves the visibility of mouse cursor
+ /// movements
+ /// by briefly showing a trail of cursors and quickly erasing them.
+ /// The pvParam parameter must point to an integer variable that receives a value. If the value is zero or 1, the
+ /// feature is disabled.
+ /// If the value is greater than 1, the feature is enabled and the value indicates the number of cursors drawn in the
+ /// trail.
+ /// The uiParam parameter is not used.
+ /// Windows 2000/NT: This value is not supported.
+ ///
+ SPI_GETMOUSETRAILS = 0x005E,
+
+ ///
+ /// Windows Me/98: Used internally; applications should not use this flag.
+ ///
+ SPI_SETSCREENSAVERRUNNING = 0x0061,
+
+ ///
+ /// Same as SPI_SETSCREENSAVERRUNNING.
+ ///
+ SPI_SCREENSAVERRUNNING = SPI_SETSCREENSAVERRUNNING,
+ //#endif /* WINVER >= 0x0400 */
+
+ ///
+ /// Retrieves information about the FilterKeys accessibility feature. The pvParam parameter must point to a FILTERKEYS
+ /// structure
+ /// that receives the information. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(FILTERKEYS).
+ ///
+ SPI_GETFILTERKEYS = 0x0032,
+
+ ///
+ /// Sets the parameters of the FilterKeys accessibility feature. The pvParam parameter must point to a FILTERKEYS
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(FILTERKEYS).
+ ///
+ SPI_SETFILTERKEYS = 0x0033,
+
+ ///
+ /// Retrieves information about the ToggleKeys accessibility feature. The pvParam parameter must point to a TOGGLEKEYS
+ /// structure
+ /// that receives the information. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(TOGGLEKEYS).
+ ///
+ SPI_GETTOGGLEKEYS = 0x0034,
+
+ ///
+ /// Sets the parameters of the ToggleKeys accessibility feature. The pvParam parameter must point to a TOGGLEKEYS
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(TOGGLEKEYS).
+ ///
+ SPI_SETTOGGLEKEYS = 0x0035,
+
+ ///
+ /// Retrieves information about the MouseKeys accessibility feature. The pvParam parameter must point to a MOUSEKEYS
+ /// structure
+ /// that receives the information. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(MOUSEKEYS).
+ ///
+ SPI_GETMOUSEKEYS = 0x0036,
+
+ ///
+ /// Sets the parameters of the MouseKeys accessibility feature. The pvParam parameter must point to a MOUSEKEYS
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(MOUSEKEYS).
+ ///
+ SPI_SETMOUSEKEYS = 0x0037,
+
+ ///
+ /// Determines whether the Show Sounds accessibility flag is on or off. If it is on, the user requires an application
+ /// to present information visually in situations where it would otherwise present the information only in audible
+ /// form.
+ /// The pvParam parameter must point to a BOOL variable that receives TRUE if the feature is on, or FALSE if it is off.
+ /// Using this value is equivalent to calling GetSystemMetrics (SM_SHOWSOUNDS). That is the recommended call.
+ ///
+ SPI_GETSHOWSOUNDS = 0x0038,
+
+ ///
+ /// Sets the parameters of the SoundSentry accessibility feature. The pvParam parameter must point to a SOUNDSENTRY
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(SOUNDSENTRY).
+ ///
+ SPI_SETSHOWSOUNDS = 0x0039,
+
+ ///
+ /// Retrieves information about the StickyKeys accessibility feature. The pvParam parameter must point to a STICKYKEYS
+ /// structure
+ /// that receives the information. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(STICKYKEYS).
+ ///
+ SPI_GETSTICKYKEYS = 0x003A,
+
+ ///
+ /// Sets the parameters of the StickyKeys accessibility feature. The pvParam parameter must point to a STICKYKEYS
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(STICKYKEYS).
+ ///
+ SPI_SETSTICKYKEYS = 0x003B,
+
+ ///
+ /// Retrieves information about the time-out period associated with the accessibility features. The pvParam parameter
+ /// must point
+ /// to an ACCESSTIMEOUT structure that receives the information. Set the cbSize member of this structure and the
+ /// uiParam parameter
+ /// to sizeof(ACCESSTIMEOUT).
+ ///
+ SPI_GETACCESSTIMEOUT = 0x003C,
+
+ ///
+ /// Sets the time-out period associated with the accessibility features. The pvParam parameter must point to an
+ /// ACCESSTIMEOUT
+ /// structure that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(ACCESSTIMEOUT).
+ ///
+ SPI_SETACCESSTIMEOUT = 0x003D,
+
+ ///
+ /// Windows Me/98/95: Retrieves information about the SerialKeys accessibility feature. The pvParam parameter must
+ /// point
+ /// to a SERIALKEYS structure that receives the information. Set the cbSize member of this structure and the uiParam
+ /// parameter
+ /// to sizeof(SERIALKEYS).
+ /// Windows Server 2003, Windows XP/2000/NT: Not supported. The user controls this feature through the control panel.
+ ///
+ SPI_GETSERIALKEYS = 0x003E,
+
+ ///
+ /// Windows Me/98/95: Sets the parameters of the SerialKeys accessibility feature. The pvParam parameter must point
+ /// to a SERIALKEYS structure that contains the new parameters. Set the cbSize member of this structure and the uiParam
+ /// parameter
+ /// to sizeof(SERIALKEYS).
+ /// Windows Server 2003, Windows XP/2000/NT: Not supported. The user controls this feature through the control panel.
+ ///
+ SPI_SETSERIALKEYS = 0x003F,
+ //#endif /* WINVER >= 0x0400 */
+
+ ///
+ /// Retrieves information about the SoundSentry accessibility feature. The pvParam parameter must point to a
+ /// SOUNDSENTRY structure
+ /// that receives the information. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(SOUNDSENTRY).
+ ///
+ SPI_GETSOUNDSENTRY = 0x0040,
+
+ ///
+ /// Sets the parameters of the SoundSentry accessibility feature. The pvParam parameter must point to a SOUNDSENTRY
+ /// structure
+ /// that contains the new parameters. Set the cbSize member of this structure and the uiParam parameter to
+ /// sizeof(SOUNDSENTRY).
+ ///
+ SPI_SETSOUNDSENTRY = 0x0041,
+
+ ///
+ /// Determines whether the snap-to-default-button feature is enabled. If enabled, the mouse cursor automatically moves
+ /// to the default button, such as OK or Apply, of a dialog box. The pvParam parameter must point to a BOOL variable
+ /// that receives TRUE if the feature is on, or FALSE if it is off.
+ /// Windows 95: Not supported.
+ ///
+ SPI_GETSNAPTODEFBUTTON = 0x005F,
+
+ ///
+ /// Enables or disables the snap-to-default-button feature. If enabled, the mouse cursor automatically moves to the
+ /// default button,
+ /// such as OK or Apply, of a dialog box. Set the uiParam parameter to TRUE to enable the feature, or FALSE to disable
+ /// it.
+ /// Applications should use the ShowWindow function when displaying a dialog box so the dialog manager can position the
+ /// mouse cursor.
+ /// Windows 95: Not supported.
+ ///
+ SPI_SETSNAPTODEFBUTTON = 0x0060,
+
+ ///
+ /// Retrieves the width, in pixels, of the rectangle within which the mouse pointer has to stay for TrackMouseEvent
+ /// to generate a WM_MOUSEHOVER message. The pvParam parameter must point to a UINT variable that receives the width.
+ /// Windows 95: Not supported.
+ ///
+ SPI_GETMOUSEHOVERWIDTH = 0x0062,
+
+ ///
+ /// Retrieves the width, in pixels, of the rectangle within which the mouse pointer has to stay for TrackMouseEvent
+ /// to generate a WM_MOUSEHOVER message. The pvParam parameter must point to a UINT variable that receives the width.
+ /// Windows 95: Not supported.
+ ///
+ SPI_SETMOUSEHOVERWIDTH = 0x0063,
+
+ ///
+ /// Retrieves the height, in pixels, of the rectangle within which the mouse pointer has to stay for TrackMouseEvent
+ /// to generate a WM_MOUSEHOVER message. The pvParam parameter must point to a UINT variable that receives the height.
+ /// Windows 95: Not supported.
+ ///
+ SPI_GETMOUSEHOVERHEIGHT = 0x0064,
+
+ ///
+ /// Sets the height, in pixels, of the rectangle within which the mouse pointer has to stay for TrackMouseEvent
+ /// to generate a WM_MOUSEHOVER message. Set the uiParam parameter to the new height.
+ /// Windows 95: Not supported.
+ ///
+ SPI_SETMOUSEHOVERHEIGHT = 0x0065,
+
+ ///
+ /// Retrieves the time, in milliseconds, that the mouse pointer has to stay in the hover rectangle for TrackMouseEvent
+ /// to generate a WM_MOUSEHOVER message. The pvParam parameter must point to a UINT variable that receives the time.
+ /// Windows 95: Not supported.
+ ///
+ SPI_GETMOUSEHOVERTIME = 0x0066,
+
+ ///
+ /// Sets the time, in milliseconds, that the mouse pointer has to stay in the hover rectangle for TrackMouseEvent
+ /// to generate a WM_MOUSEHOVER message. This is used only if you pass HOVER_DEFAULT in the dwHoverTime parameter in
+ /// the call to TrackMouseEvent. Set the uiParam parameter to the new time.
+ /// Windows 95: Not supported.
+ ///
+ SPI_SETMOUSEHOVERTIME = 0x0067,
+
+ ///
+ /// Retrieves the number of lines to scroll when the mouse wheel is rotated. The pvParam parameter must point
+ /// to a UINT variable that receives the number of lines. The default value is 3.
+ /// Windows 95: Not supported.
+ ///
+ SPI_GETWHEELSCROLLLINES = 0x0068,
+
+ ///
+ /// Sets the number of lines to scroll when the mouse wheel is rotated. The number of lines is set from the uiParam
+ /// parameter.
+ /// The number of lines is the suggested number of lines to scroll when the mouse wheel is rolled without using
+ /// modifier keys.
+ /// If the number is 0, then no scrolling should occur. If the number of lines to scroll is greater than the number of
+ /// lines viewable,
+ /// and in particular if it is WHEEL_PAGESCROLL (#defined as UINT_MAX), the scroll operation should be interpreted
+ /// as clicking once in the page down or page up regions of the scroll bar.
+ /// Windows 95: Not supported.
+ ///
+ SPI_SETWHEELSCROLLLINES = 0x0069,
+
+ ///
+ /// Retrieves the time, in milliseconds, that the system waits before displaying a shortcut menu when the mouse cursor
+ /// is
+ /// over a submenu item. The pvParam parameter must point to a DWORD variable that receives the time of the delay.
+ /// Windows 95: Not supported.
+ ///
+ SPI_GETMENUSHOWDELAY = 0x006A,
+
+ ///
+ /// Sets uiParam to the time, in milliseconds, that the system waits before displaying a shortcut menu when the mouse
+ /// cursor is
+ /// over a submenu item.
+ /// Windows 95: Not supported.
+ ///
+ SPI_SETMENUSHOWDELAY = 0x006B,
+
+ ///
+ /// Determines whether the IME status window is visible (on a per-user basis). The pvParam parameter must point to a
+ /// BOOL variable
+ /// that receives TRUE if the status window is visible, or FALSE if it is not.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETSHOWIMEUI = 0x006E,
+
+ ///
+ /// Sets whether the IME status window is visible or not on a per-user basis. The uiParam parameter specifies TRUE for
+ /// on or FALSE for off.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETSHOWIMEUI = 0x006F,
+
+ ///
+ /// Retrieves the current mouse speed. The mouse speed determines how far the pointer will move based on the distance
+ /// the mouse moves.
+ /// The pvParam parameter must point to an integer that receives a value which ranges between 1 (slowest) and 20
+ /// (fastest).
+ /// A value of 10 is the default. The value can be set by an end user using the mouse control panel application or
+ /// by an application using SPI_SETMOUSESPEED.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETMOUSESPEED = 0x0070,
+
+ ///
+ /// Sets the current mouse speed. The pvParam parameter is an integer between 1 (slowest) and 20 (fastest). A value of
+ /// 10 is the default.
+ /// This value is typically set using the mouse control panel application.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETMOUSESPEED = 0x0071,
+
+ ///
+ /// Determines whether a screen saver is currently running on the window station of the calling process.
+ /// The pvParam parameter must point to a BOOL variable that receives TRUE if a screen saver is currently running, or
+ /// FALSE otherwise.
+ /// Note that only the interactive window station, "WinSta0", can have a screen saver running.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETSCREENSAVERRUNNING = 0x0072,
+
+ ///
+ /// Retrieves the full path of the bitmap file for the desktop wallpaper. The pvParam parameter must point to a buffer
+ /// that receives a null-terminated path string. Set the uiParam parameter to the size, in characters, of the pvParam
+ /// buffer. The returned string will not exceed MAX_PATH characters. If there is no desktop wallpaper, the returned
+ /// string is empty.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETDESKWALLPAPER = 0x0073,
+
+ ///
+ /// Determines whether active window tracking (activating the window the mouse is on) is on or off. The pvParam
+ /// parameter must point
+ /// to a BOOL variable that receives TRUE for on, or FALSE for off.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETACTIVEWINDOWTRACKING = 0x1000,
+
+ ///
+ /// Sets active window tracking (activating the window the mouse is on) either on or off. Set pvParam to TRUE for on or
+ /// FALSE for off.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETACTIVEWINDOWTRACKING = 0x1001,
+
+ ///
+ /// Determines whether the menu animation feature is enabled. This master switch must be on to enable menu animation
+ /// effects.
+ /// The pvParam parameter must point to a BOOL variable that receives TRUE if animation is enabled and FALSE if it is
+ /// disabled.
+ /// If animation is enabled, SPI_GETMENUFADE indicates whether menus use fade or slide animation.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETMENUANIMATION = 0x1002,
+
+ ///
+ /// Enables or disables menu animation. This master switch must be on for any menu animation to occur.
+ /// The pvParam parameter is a BOOL variable; set pvParam to TRUE to enable animation and FALSE to disable animation.
+ /// If animation is enabled, SPI_GETMENUFADE indicates whether menus use fade or slide animation.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETMENUANIMATION = 0x1003,
+
+ ///
+ /// Determines whether the slide-open effect for combo boxes is enabled. The pvParam parameter must point to a BOOL
+ /// variable
+ /// that receives TRUE for enabled, or FALSE for disabled.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETCOMBOBOXANIMATION = 0x1004,
+
+ ///
+ /// Enables or disables the slide-open effect for combo boxes. Set the pvParam parameter to TRUE to enable the gradient
+ /// effect,
+ /// or FALSE to disable it.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETCOMBOBOXANIMATION = 0x1005,
+
+ ///
+ /// Determines whether the smooth-scrolling effect for list boxes is enabled. The pvParam parameter must point to a
+ /// BOOL variable
+ /// that receives TRUE for enabled, or FALSE for disabled.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETLISTBOXSMOOTHSCROLLING = 0x1006,
+
+ ///
+ /// Enables or disables the smooth-scrolling effect for list boxes. Set the pvParam parameter to TRUE to enable the
+ /// smooth-scrolling effect,
+ /// or FALSE to disable it.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETLISTBOXSMOOTHSCROLLING = 0x1007,
+
+ ///
+ /// Determines whether the gradient effect for window title bars is enabled. The pvParam parameter must point to a BOOL
+ /// variable
+ /// that receives TRUE for enabled, or FALSE for disabled. For more information about the gradient effect, see the
+ /// GetSysColor function.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETGRADIENTCAPTIONS = 0x1008,
+
+ ///
+ /// Enables or disables the gradient effect for window title bars. Set the pvParam parameter to TRUE to enable it, or
+ /// FALSE to disable it.
+ /// The gradient effect is possible only if the system has a color depth of more than 256 colors. For more information
+ /// about
+ /// the gradient effect, see the GetSysColor function.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETGRADIENTCAPTIONS = 0x1009,
+
+ ///
+ /// Determines whether menu access keys are always underlined. The pvParam parameter must point to a BOOL variable that
+ /// receives TRUE
+ /// if menu access keys are always underlined, and FALSE if they are underlined only when the menu is activated by the
+ /// keyboard.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETKEYBOARDCUES = 0x100A,
+
+ ///
+ /// Sets the underlining of menu access key letters. The pvParam parameter is a BOOL variable. Set pvParam to TRUE to
+ /// always underline menu
+ /// access keys, or FALSE to underline menu access keys only when the menu is activated from the keyboard.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETKEYBOARDCUES = 0x100B,
+
+ ///
+ /// Same as SPI_GETKEYBOARDCUES.
+ ///
+ SPI_GETMENUUNDERLINES = SPI_GETKEYBOARDCUES,
+
+ ///
+ /// Same as SPI_SETKEYBOARDCUES.
+ ///
+ SPI_SETMENUUNDERLINES = SPI_SETKEYBOARDCUES,
+
+ ///
+ /// Determines whether windows activated through active window tracking will be brought to the top. The pvParam
+ /// parameter must point
+ /// to a BOOL variable that receives TRUE for on, or FALSE for off.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETACTIVEWNDTRKZORDER = 0x100C,
+
+ ///
+ /// Determines whether or not windows activated through active window tracking should be brought to the top. Set
+ /// pvParam to TRUE
+ /// for on or FALSE for off.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETACTIVEWNDTRKZORDER = 0x100D,
+
+ ///
+ /// Determines whether hot tracking of user-interface elements, such as menu names on menu bars, is enabled. The
+ /// pvParam parameter
+ /// must point to a BOOL variable that receives TRUE for enabled, or FALSE for disabled.
+ /// Hot tracking means that when the cursor moves over an item, it is highlighted but not selected. You can query this
+ /// value to decide
+ /// whether to use hot tracking in the user interface of your application.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETHOTTRACKING = 0x100E,
+
+ ///
+ /// Enables or disables hot tracking of user-interface elements such as menu names on menu bars. Set the pvParam
+ /// parameter to TRUE
+ /// to enable it, or FALSE to disable it.
+ /// Hot-tracking means that when the cursor moves over an item, it is highlighted but not selected.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETHOTTRACKING = 0x100F,
+
+ ///
+ /// Determines whether menu fade animation is enabled. The pvParam parameter must point to a BOOL variable that
+ /// receives TRUE
+ /// when fade animation is enabled and FALSE when it is disabled. If fade animation is disabled, menus use slide
+ /// animation.
+ /// This flag is ignored unless menu animation is enabled, which you can do using the SPI_SETMENUANIMATION flag.
+ /// For more information, see AnimateWindow.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETMENUFADE = 0x1012,
+
+ ///
+ /// Enables or disables menu fade animation. Set pvParam to TRUE to enable the menu fade effect or FALSE to disable it.
+ /// If fade animation is disabled, menus use slide animation. he The menu fade effect is possible only if the system
+ /// has a color depth of more than 256 colors. This flag is ignored unless SPI_MENUANIMATION is also set. For more
+ /// information,
+ /// see AnimateWindow.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETMENUFADE = 0x1013,
+
+ ///
+ /// Determines whether the selection fade effect is enabled. The pvParam parameter must point to a BOOL variable that
+ /// receives TRUE
+ /// if enabled or FALSE if disabled.
+ /// The selection fade effect causes the menu item selected by the user to remain on the screen briefly while fading
+ /// out
+ /// after the menu is dismissed.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETSELECTIONFADE = 0x1014,
+
+ ///
+ /// Set pvParam to TRUE to enable the selection fade effect or FALSE to disable it.
+ /// The selection fade effect causes the menu item selected by the user to remain on the screen briefly while fading
+ /// out
+ /// after the menu is dismissed. The selection fade effect is possible only if the system has a color depth of more
+ /// than 256 colors.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETSELECTIONFADE = 0x1015,
+
+ ///
+ /// Determines whether ToolTip animation is enabled. The pvParam parameter must point to a BOOL variable that receives
+ /// TRUE
+ /// if enabled or FALSE if disabled. If ToolTip animation is enabled, SPI_GETTOOLTIPFADE indicates whether ToolTips use
+ /// fade or slide animation.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETTOOLTIPANIMATION = 0x1016,
+
+ ///
+ /// Set pvParam to TRUE to enable ToolTip animation or FALSE to disable it. If enabled, you can use SPI_SETTOOLTIPFADE
+ /// to specify fade or slide animation.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETTOOLTIPANIMATION = 0x1017,
+
+ ///
+ /// If SPI_SETTOOLTIPANIMATION is enabled, SPI_GETTOOLTIPFADE indicates whether ToolTip animation uses a fade effect or
+ /// a slide effect.
+ /// The pvParam parameter must point to a BOOL variable that receives TRUE for fade animation or FALSE for slide
+ /// animation.
+ /// For more information on slide and fade effects, see AnimateWindow.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETTOOLTIPFADE = 0x1018,
+
+ ///
+ /// If the SPI_SETTOOLTIPANIMATION flag is enabled, use SPI_SETTOOLTIPFADE to indicate whether ToolTip animation uses a
+ /// fade effect
+ /// or a slide effect. Set pvParam to TRUE for fade animation or FALSE for slide animation. The tooltip fade effect is
+ /// possible only
+ /// if the system has a color depth of more than 256 colors. For more information on the slide and fade effects,
+ /// see the AnimateWindow function.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETTOOLTIPFADE = 0x1019,
+
+ ///
+ /// Determines whether the cursor has a shadow around it. The pvParam parameter must point to a BOOL variable that
+ /// receives TRUE
+ /// if the shadow is enabled, FALSE if it is disabled. This effect appears only if the system has a color depth of more
+ /// than 256 colors.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETCURSORSHADOW = 0x101A,
+
+ ///
+ /// Enables or disables a shadow around the cursor. The pvParam parameter is a BOOL variable. Set pvParam to TRUE to
+ /// enable the shadow
+ /// or FALSE to disable the shadow. This effect appears only if the system has a color depth of more than 256 colors.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETCURSORSHADOW = 0x101B,
+
+ ///
+ /// Retrieves the state of the Mouse Sonar feature. The pvParam parameter must point to a BOOL variable that receives
+ /// TRUE
+ /// if enabled or FALSE otherwise. For more information, see About Mouse Input on MSDN.
+ /// Windows 2000/NT, Windows 98/95: This value is not supported.
+ ///
+ SPI_GETMOUSESONAR = 0x101C,
+
+ ///
+ /// Turns the Sonar accessibility feature on or off. This feature briefly shows several concentric circles around the
+ /// mouse pointer when the user presses and releases the CTRL key. The pvParam parameter specifies TRUE for on and FALSE for off.
+ /// The default is off.
+ /// For more information, see About Mouse Input.
+ /// Windows 2000/NT, Windows 98/95: This value is not supported.
+ ///
+ SPI_SETMOUSESONAR = 0x101D,
+
+ ///
+ /// Retrieves the state of the Mouse ClickLock feature. The pvParam parameter must point to a BOOL variable that
+ /// receives TRUE
+ /// if enabled, or FALSE otherwise. For more information, see About Mouse Input.
+ /// Windows 2000/NT, Windows 98/95: This value is not supported.
+ ///
+ SPI_GETMOUSECLICKLOCK = 0x101E,
+
+ ///
+ /// Turns the Mouse ClickLock accessibility feature on or off. This feature temporarily locks down the primary mouse
+ /// button
+ /// when that button is clicked and held down for the time specified by SPI_SETMOUSECLICKLOCKTIME. The uiParam
+ /// parameter specifies
+ /// TRUE for on,
+ /// or FALSE for off. The default is off. For more information, see Remarks and About Mouse Input on MSDN.
+ /// Windows 2000/NT, Windows 98/95: This value is not supported.
+ ///
+ SPI_SETMOUSECLICKLOCK = 0x101F,
+
+ ///
+ /// Retrieves the state of the Mouse Vanish feature. The pvParam parameter must point to a BOOL variable that receives
+ /// TRUE
+ /// if enabled or FALSE otherwise. For more information, see About Mouse Input on MSDN.
+ /// Windows 2000/NT, Windows 98/95: This value is not supported.
+ ///
+ SPI_GETMOUSEVANISH = 0x1020,
+
+ ///
+ /// Turns the Vanish feature on or off. This feature hides the mouse pointer when the user types; the pointer reappears
+ /// when the user moves the mouse. The pvParam parameter specifies TRUE for on and FALSE for off. The default is off.
+ /// For more information, see About Mouse Input on MSDN.
+ /// Windows 2000/NT, Windows 98/95: This value is not supported.
+ ///
+ SPI_SETMOUSEVANISH = 0x1021,
+
+ ///
+ /// Determines whether native User menus have flat menu appearance. The pvParam parameter must point to a BOOL variable
+ /// that returns TRUE if the flat menu appearance is set, or FALSE otherwise.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETFLATMENU = 0x1022,
+
+ ///
+ /// Enables or disables flat menu appearance for native User menus. Set pvParam to TRUE to enable flat menu appearance
+ /// or FALSE to disable it.
+ /// When enabled, the menu bar uses COLOR_MENUBAR for the menubar background, COLOR_MENU for the menu-popup background,
+ /// COLOR_MENUHILIGHT
+ /// for the fill of the current menu selection, and COLOR_HILIGHT for the outline of the current menu selection.
+ /// If disabled, menus are drawn using the same metrics and colors as in Windows 2000 and earlier.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETFLATMENU = 0x1023,
+
+ ///
+ /// Determines whether the drop shadow effect is enabled. The pvParam parameter must point to a BOOL variable that
+ /// returns TRUE
+ /// if enabled or FALSE if disabled.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETDROPSHADOW = 0x1024,
+
+ ///
+ /// Enables or disables the drop shadow effect. Set pvParam to TRUE to enable the drop shadow effect or FALSE to
+ /// disable it.
+ /// You must also have CS_DROPSHADOW in the window class style.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETDROPSHADOW = 0x1025,
+
+ ///
+ /// Retrieves a BOOL indicating whether an application can reset the screensaver's timer by calling the SendInput
+ /// function
+ /// to simulate keyboard or mouse input. The pvParam parameter must point to a BOOL variable that receives TRUE
+ /// if the simulated input will be blocked, or FALSE otherwise.
+ ///
+ SPI_GETBLOCKSENDINPUTRESETS = 0x1026,
+
+ ///
+ /// Determines whether an application can reset the screensaver's timer by calling the SendInput function to simulate
+ /// keyboard
+ /// or mouse input. The uiParam parameter specifies TRUE if the screensaver will not be deactivated by simulated input,
+ /// or FALSE if the screensaver will be deactivated by simulated input.
+ ///
+ SPI_SETBLOCKSENDINPUTRESETS = 0x1027,
+ //#endif /* _WIN32_WINNT >= 0x0501 */
+
+ ///
+ /// Determines whether UI effects are enabled or disabled. The pvParam parameter must point to a BOOL variable that
+ /// receives TRUE
+ /// if all UI effects are enabled, or FALSE if they are disabled.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETUIEFFECTS = 0x103E,
+
+ ///
+ /// Enables or disables UI effects. Set the pvParam parameter to TRUE to enable all UI effects or FALSE to disable all
+ /// UI effects.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETUIEFFECTS = 0x103F,
+
+ ///
+ /// Retrieves the amount of time following user input, in milliseconds, during which the system will not allow
+ /// applications
+ /// to force themselves into the foreground. The pvParam parameter must point to a DWORD variable that receives the
+ /// time.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000,
+
+ ///
+ /// Sets the amount of time following user input, in milliseconds, during which the system does not allow applications
+ /// to force themselves into the foreground. Set pvParam to the new timeout value.
+ /// The calling thread must be able to change the foreground window, otherwise the call fails.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001,
+
+ ///
+ /// Retrieves the active window tracking delay, in milliseconds. The pvParam parameter must point to a DWORD variable
+ /// that receives the time.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETACTIVEWNDTRKTIMEOUT = 0x2002,
+
+ ///
+ /// Sets the active window tracking delay. Set pvParam to the number of milliseconds to delay before activating the
+ /// window
+ /// under the mouse pointer.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETACTIVEWNDTRKTIMEOUT = 0x2003,
+
+ ///
+ /// Retrieves the number of times SetForegroundWindow will flash the taskbar button when rejecting a foreground switch
+ /// request.
+ /// The pvParam parameter must point to a DWORD variable that receives the value.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_GETFOREGROUNDFLASHCOUNT = 0x2004,
+
+ ///
+ /// Sets the number of times SetForegroundWindow will flash the taskbar button when rejecting a foreground switch
+ /// request.
+ /// Set pvParam to the number of times to flash.
+ /// Windows NT, Windows 95: This value is not supported.
+ ///
+ SPI_SETFOREGROUNDFLASHCOUNT = 0x2005,
+
+ ///
+ /// Retrieves the caret width in edit controls, in pixels. The pvParam parameter must point to a DWORD that receives
+ /// this value.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETCARETWIDTH = 0x2006,
+
+ ///
+ /// Sets the caret width in edit controls. Set pvParam to the desired width, in pixels. The default and minimum value
+ /// is 1.
+ /// Windows NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETCARETWIDTH = 0x2007,
+
+ ///
+ /// Retrieves the time delay before the primary mouse button is locked. The pvParam parameter must point to DWORD that
+ /// receives
+ /// the time delay. This is only enabled if SPI_SETMOUSECLICKLOCK is set to TRUE. For more information, see About Mouse
+ /// Input on MSDN.
+ /// Windows 2000/NT, Windows 98/95: This value is not supported.
+ ///
+ SPI_GETMOUSECLICKLOCKTIME = 0x2008,
+
+ ///
+ /// Turns the Mouse ClickLock accessibility feature on or off. This feature temporarily locks down the primary mouse
+ /// button
+ /// when that button is clicked and held down for the time specified by SPI_SETMOUSECLICKLOCKTIME. The uiParam
+ /// parameter
+ /// specifies TRUE for on, or FALSE for off. The default is off. For more information, see Remarks and About Mouse
+ /// Input on MSDN.
+ /// Windows 2000/NT, Windows 98/95: This value is not supported.
+ ///
+ SPI_SETMOUSECLICKLOCKTIME = 0x2009,
+
+ ///
+ /// Retrieves the type of font smoothing. The pvParam parameter must point to a UINT that receives the information.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETFONTSMOOTHINGTYPE = 0x200A,
+
+ ///
+ /// Sets the font smoothing type. The pvParam parameter points to a UINT that contains either FE_FONTSMOOTHINGSTANDARD,
+ /// if standard anti-aliasing is used, or FE_FONTSMOOTHINGCLEARTYPE, if ClearType is used. The default is
+ /// FE_FONTSMOOTHINGSTANDARD.
+ /// When using this option, the fWinIni parameter must be set to SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE; otherwise,
+ /// SystemParametersInfo fails.
+ ///
+ SPI_SETFONTSMOOTHINGTYPE = 0x200B,
+
+ ///
+ /// Retrieves a contrast value that is used in ClearType™ smoothing. The pvParam parameter must point to a UINT
+ /// that receives the information.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETFONTSMOOTHINGCONTRAST = 0x200C,
+
+ ///
+ /// Sets the contrast value used in ClearType smoothing. The pvParam parameter points to a UINT that holds the contrast
+ /// value.
+ /// Valid contrast values are from 1000 to 2200. The default value is 1400.
+ /// When using this option, the fWinIni parameter must be set to SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE; otherwise,
+ /// SystemParametersInfo fails.
+ /// SPI_SETFONTSMOOTHINGTYPE must also be set to FE_FONTSMOOTHINGCLEARTYPE.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETFONTSMOOTHINGCONTRAST = 0x200D,
+
+ ///
+ /// Retrieves the width, in pixels, of the left and right edges of the focus rectangle drawn with DrawFocusRect.
+ /// The pvParam parameter must point to a UINT.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETFOCUSBORDERWIDTH = 0x200E,
+
+ ///
+ /// Sets the height of the left and right edges of the focus rectangle drawn with DrawFocusRect to the value of the
+ /// pvParam parameter.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETFOCUSBORDERWIDTH = 0x200F,
+
+ ///
+ /// Retrieves the height, in pixels, of the top and bottom edges of the focus rectangle drawn with DrawFocusRect.
+ /// The pvParam parameter must point to a UINT.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_GETFOCUSBORDERHEIGHT = 0x2010,
+
+ ///
+ /// Sets the height of the top and bottom edges of the focus rectangle drawn with DrawFocusRect to the value of the
+ /// pvParam parameter.
+ /// Windows 2000/NT, Windows Me/98/95: This value is not supported.
+ ///
+ SPI_SETFOCUSBORDERHEIGHT = 0x2011,
+
+ ///
+ /// Not implemented.
+ ///
+ SPI_GETFONTSMOOTHINGORIENTATION = 0x2012,
+
+ ///
+ /// Not implemented.
+ ///
+ SPI_SETFONTSMOOTHINGORIENTATION = 0x2013
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/Enums/SystemParametersInfoBehaviors.cs b/GreenshotPlugin/Core/Enums/SystemParametersInfoBehaviors.cs
new file mode 100644
index 000000000..4b5872ae5
--- /dev/null
+++ b/GreenshotPlugin/Core/Enums/SystemParametersInfoBehaviors.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Dapplo and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace GreenshotPlugin.Core.Enums
+{
+ ///
+ /// If a system parameter is being set, specifies whether the user profile is to be updated, and if so, whether the
+ /// WM_SETTINGCHANGE message is to be broadcast to all top-level windows to notify them of the change.
+ /// This parameter can be zero if you do not want to update the user profile or broadcast the WM_SETTINGCHANGE message,
+ /// or it can be one or more of the following values.
+ ///
+ public enum SystemParametersInfoBehaviors : uint
+ {
+ ///
+ /// Do nothing
+ ///
+ None = 0x00,
+
+ /// Writes the new system-wide parameter setting to the user profile.
+ UpdateIniFile = 0x01,
+
+ /// Broadcasts the WM_SETTINGCHANGE message after updating the user profile.
+ SendChange = 0x02,
+
+ /// Same as SPIF_SENDCHANGE.
+ SendWinIniChange = SendChange
+ }
+}
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/EventDelay.cs b/GreenshotPlugin/Core/EventDelay.cs
new file mode 100644
index 000000000..84860f7ae
--- /dev/null
+++ b/GreenshotPlugin/Core/EventDelay.cs
@@ -0,0 +1,41 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+
+namespace GreenshotPlugin.Core {
+ public class EventDelay {
+ private long lastCheck;
+ private readonly long waitTime;
+ public EventDelay(long ticks) {
+ waitTime = ticks;
+ }
+
+ public bool Check() {
+ lock (this) {
+ long now = DateTime.Now.Ticks;
+ bool isPassed = now - lastCheck > waitTime;
+ lastCheck = now;
+ return isPassed;
+ }
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/ExplorerHelper.cs b/GreenshotPlugin/Core/ExplorerHelper.cs
new file mode 100644
index 000000000..8f67018b1
--- /dev/null
+++ b/GreenshotPlugin/Core/ExplorerHelper.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace GreenshotPlugin.Core
+{
+ ///
+ /// Simple utility for the explorer
+ ///
+ public static class ExplorerHelper
+ {
+ ///
+ /// Open the path in the windows explorer.
+ /// If the path is a directory, it will just open the explorer with that directory.
+ /// If the path is a file, the explorer is opened with the directory and the file is selected.
+ ///
+ /// Path to file or directory
+ public static bool OpenInExplorer(string path)
+ {
+ if (path == null)
+ {
+ return false;
+ }
+ try
+ {
+ // Check if path is a directory
+ if (Directory.Exists(path))
+ {
+ using (Process.Start(path))
+ {
+ return true;
+ }
+ }
+ // Check if path is a file
+ if (File.Exists(path))
+ {
+ // Start the explorer process and select the file
+ using var explorer = Process.Start("explorer.exe", $"/select,\"{path}\"");
+ explorer?.WaitForInputIdle(500);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ // Make sure we show what we tried to open in the exception
+ ex.Data.Add("path", path);
+ throw;
+ }
+ return false;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/FastBitmap.cs b/GreenshotPlugin/Core/FastBitmap.cs
new file mode 100644
index 000000000..a76045a42
--- /dev/null
+++ b/GreenshotPlugin/Core/FastBitmap.cs
@@ -0,0 +1,1019 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace GreenshotPlugin.Core {
+
+ ///
+ /// The interface for the FastBitmap
+ ///
+ public interface IFastBitmap : IDisposable {
+ ///
+ /// Get the color at x,y
+ /// The returned Color object depends on the underlying pixel format
+ ///
+ /// int x
+ /// int y
+ /// Color
+ Color GetColorAt(int x, int y);
+
+ ///
+ /// Set the color at the specified location
+ ///
+ /// int x
+ /// int y
+ /// Color
+ void SetColorAt(int x, int y, Color color);
+
+ ///
+ /// Get the color at x,y
+ /// The returned byte[] color depends on the underlying pixel format
+ ///
+ /// int x
+ /// int y
+ /// byte array
+ void GetColorAt(int x, int y, byte[] color);
+
+ ///
+ /// Set the color at the specified location
+ ///
+ /// int x
+ /// int y
+ /// byte[] color
+ void SetColorAt(int x, int y, byte[] color);
+
+ ///
+ /// Lock the bitmap
+ ///
+ void Lock();
+
+ ///
+ /// Unlock the bitmap
+ ///
+ void Unlock();
+
+ ///
+ /// Unlock the bitmap and get the underlying bitmap in one call
+ ///
+ ///
+ Bitmap UnlockAndReturnBitmap();
+
+ ///
+ /// Size of the underlying image
+ ///
+ Size Size {
+ get;
+ }
+
+ ///
+ /// Height of the image area that this fastbitmap covers
+ ///
+ int Height {
+ get;
+ }
+
+ ///
+ /// Width of the image area that this fastbitmap covers
+ ///
+ int Width {
+ get;
+ }
+
+ ///
+ /// Top of the image area that this fastbitmap covers
+ ///
+ int Top {
+ get;
+ }
+
+ ///
+ /// Left of the image area that this fastbitmap covers
+ ///
+ int Left {
+ get;
+ }
+
+ ///
+ /// Right of the image area that this fastbitmap covers
+ ///
+ int Right {
+ get;
+ }
+
+ ///
+ /// Bottom of the image area that this fastbitmap covers
+ ///
+ int Bottom {
+ get;
+ }
+
+ ///
+ /// Does the underlying image need to be disposed
+ ///
+ bool NeedsDispose {
+ get;
+ set;
+ }
+
+ ///
+ /// Returns if this FastBitmap has an alpha channel
+ ///
+ bool HasAlphaChannel {
+ get;
+ }
+
+ ///
+ /// Draw the stored bitmap to the destionation bitmap at the supplied point
+ ///
+ /// Graphics
+ /// Point with location
+ void DrawTo(Graphics graphics, Point destination);
+
+ ///
+ /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
+ /// Be aware that the stored bitmap will be resized to the specified rectangle!!
+ ///
+ /// Graphics
+ /// Rectangle with destination
+ void DrawTo(Graphics graphics, Rectangle destinationRect);
+
+ ///
+ /// Return true if the coordinates are inside the FastBitmap
+ ///
+ ///
+ ///
+ ///
+ bool Contains(int x, int y);
+
+ ///
+ /// Set the bitmap resolution
+ ///
+ ///
+ ///
+ void SetResolution(float horizontal, float vertical);
+ }
+
+ ///
+ /// This interface can be used for when offsetting is needed
+ ///
+ public interface IFastBitmapWithOffset : IFastBitmap {
+ ///
+ /// Return true if the coordinates are inside the FastBitmap
+ ///
+ ///
+ ///
+ ///
+ new bool Contains(int x, int y);
+
+ ///
+ /// Set the color at the specified location, using offsetting so the original coordinates can be used
+ ///
+ /// int x
+ /// int y
+ /// Color color
+ new void SetColorAt(int x, int y, Color color);
+
+ ///
+ /// Set the color at the specified location, using offsetting so the original coordinates can be used
+ ///
+ /// int x
+ /// int y
+ /// byte[] color
+ new void SetColorAt(int x, int y, byte[] color);
+
+ ///
+ /// Get the color at x,y
+ /// The returned Color object depends on the underlying pixel format
+ ///
+ /// int x
+ /// int y
+ /// Color
+ new Color GetColorAt(int x, int y);
+
+ ///
+ /// Get the color at x,y, using offsetting so the original coordinates can be used
+ /// The returned byte[] color depends on the underlying pixel format
+ ///
+ /// int x
+ /// int y
+ /// byte array
+ new void GetColorAt(int x, int y, byte[] color);
+
+ new int Left {
+ get;
+ set;
+ }
+
+ new int Top {
+ get;
+ set;
+ }
+ }
+
+ ///
+ /// This interface can be used for when clipping is needed
+ ///
+ public interface IFastBitmapWithClip : IFastBitmap {
+ Rectangle Clip {
+ get;
+ set;
+ }
+
+ bool InvertClip {
+ get;
+ set;
+ }
+
+ ///
+ /// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
+ ///
+ /// int x
+ /// int y
+ /// Color color
+ new void SetColorAt(int x, int y, Color color);
+
+ ///
+ /// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
+ ///
+ /// int x
+ /// int y
+ /// byte[] color
+ new void SetColorAt(int x, int y, byte[] color);
+
+ ///
+ /// Return true if the coordinates are inside the FastBitmap and not clipped
+ ///
+ ///
+ ///
+ ///
+ new bool Contains(int x, int y);
+ }
+
+ ///
+ /// This interface is implemented when there is a alpha-blending possibility
+ ///
+ public interface IFastBitmapWithBlend : IFastBitmap {
+ Color BackgroundBlendColor {
+ get;
+ set;
+ }
+ Color GetBlendedColorAt(int x, int y);
+ }
+
+ ///
+ /// The base class for the fast bitmap implementation
+ ///
+ public abstract unsafe class FastBitmap : IFastBitmapWithClip, IFastBitmapWithOffset {
+ protected const int PixelformatIndexA = 3;
+ protected const int PixelformatIndexR = 2;
+ protected const int PixelformatIndexG = 1;
+ protected const int PixelformatIndexB = 0;
+
+ public const int ColorIndexR = 0;
+ public const int ColorIndexG = 1;
+ public const int ColorIndexB = 2;
+ public const int ColorIndexA = 3;
+
+ protected Rectangle Area;
+ ///
+ /// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap
+ ///
+ public bool NeedsDispose {
+ get;
+ set;
+ }
+
+ public Rectangle Clip {
+ get;
+ set;
+ }
+
+ public bool InvertClip {
+ get;
+ set;
+ }
+
+ ///
+ /// The bitmap for which the FastBitmap is creating access
+ ///
+ protected Bitmap Bitmap;
+
+ protected BitmapData BmData;
+ protected int Stride; /* bytes per pixel row */
+ protected bool BitsLocked;
+ protected byte* Pointer;
+
+ public static IFastBitmap Create(Bitmap source) {
+ return Create(source, Rectangle.Empty);
+ }
+
+ public void SetResolution(float horizontal, float vertical) {
+ Bitmap.SetResolution(horizontal, vertical);
+ }
+
+ ///
+ /// Factory for creating a FastBitmap depending on the pixelformat of the source
+ /// The supplied rectangle specifies the area for which the FastBitmap does its thing
+ ///
+ /// Bitmap to access
+ /// Rectangle which specifies the area to have access to, can be Rectangle.Empty for the whole image
+ /// IFastBitmap
+ public static IFastBitmap Create(Bitmap source, Rectangle area) {
+ switch (source.PixelFormat) {
+ case PixelFormat.Format8bppIndexed:
+ return new FastChunkyBitmap(source, area);
+ case PixelFormat.Format24bppRgb:
+ return new Fast24RgbBitmap(source, area);
+ case PixelFormat.Format32bppRgb:
+ return new Fast32RgbBitmap(source, area);
+ case PixelFormat.Format32bppArgb:
+ case PixelFormat.Format32bppPArgb:
+ return new Fast32ArgbBitmap(source, area);
+ default:
+ throw new NotSupportedException($"Not supported Pixelformat {source.PixelFormat}");
+ }
+ }
+
+ ///
+ /// Factory for creating a FastBitmap as a destination for the source
+ ///
+ /// Bitmap to clone
+ /// IFastBitmap
+ public static IFastBitmap CreateCloneOf(Image source) {
+ return CreateCloneOf(source, source.PixelFormat, Rectangle.Empty);
+ }
+
+ ///
+ /// Factory for creating a FastBitmap as a destination for the source
+ ///
+ /// Bitmap to clone
+ /// new Pixelformat
+ /// IFastBitmap
+ public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat) {
+ return CreateCloneOf(source, pixelFormat, Rectangle.Empty);
+ }
+ ///
+ /// Factory for creating a FastBitmap as a destination for the source
+ ///
+ /// Bitmap to clone
+ /// Area of the bitmap to access, can be Rectangle.Empty for the whole
+ /// IFastBitmap
+ public static IFastBitmap CreateCloneOf(Image source, Rectangle area) {
+ return CreateCloneOf(source, PixelFormat.DontCare, area);
+ }
+
+ ///
+ /// Factory for creating a FastBitmap as a destination for the source
+ ///
+ /// Bitmap to clone
+ /// Pixelformat of the cloned bitmap
+ /// Area of the bitmap to access, can be Rectangle.Empty for the whole
+ /// IFastBitmap
+ public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area) {
+ Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat);
+ FastBitmap fastBitmap = Create(destination) as FastBitmap;
+ if (fastBitmap != null)
+ {
+ fastBitmap.NeedsDispose = true;
+ fastBitmap.Left = area.Left;
+ fastBitmap.Top = area.Top;
+ }
+ return fastBitmap;
+ }
+
+ ///
+ /// Factory for creating a FastBitmap as a destination
+ ///
+ ///
+ ///
+ ///
+ /// IFastBitmap
+ public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor) {
+ Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f);
+ IFastBitmap fastBitmap = Create(destination);
+ fastBitmap.NeedsDispose = true;
+ return fastBitmap;
+ }
+
+ ///
+ /// Constructor which stores the image and locks it when called
+ ///
+ /// Bitmap
+ /// Rectangle
+ protected FastBitmap(Bitmap bitmap, Rectangle area) {
+ Bitmap = bitmap;
+ Rectangle bitmapArea = new Rectangle(Point.Empty, bitmap.Size);
+ if (area != Rectangle.Empty) {
+ area.Intersect(bitmapArea);
+ Area = area;
+ } else {
+ Area = bitmapArea;
+ }
+ // As the lock takes care that only the specified area is made available we need to calculate the offset
+ Left = area.Left;
+ Top = area.Top;
+ // Default cliping is done to the area without invert
+ Clip = Area;
+ InvertClip = false;
+ // Always lock, so we don't need to do this ourselves
+ Lock();
+ }
+
+ ///
+ /// Return the size of the image
+ ///
+ public Size Size {
+ get {
+ if (Area == Rectangle.Empty) {
+ return Bitmap.Size;
+ }
+ return Area.Size;
+ }
+ }
+
+ ///
+ /// Return the width of the image
+ ///
+ public int Width {
+ get {
+ if (Area == Rectangle.Empty) {
+ return Bitmap.Width;
+ }
+ return Area.Width;
+ }
+ }
+
+ ///
+ /// Return the height of the image
+ ///
+ public int Height {
+ get {
+ if (Area == Rectangle.Empty) {
+ return Bitmap.Height;
+ }
+ return Area.Height;
+ }
+ }
+
+ private int _left;
+ ///
+ /// Return the left of the fastbitmap, this is also used as an offset
+ ///
+ public int Left {
+ get {
+ return 0;
+ }
+ set {
+ _left = value;
+ }
+ }
+
+ ///
+ /// Return the left of the fastbitmap, this is also used as an offset
+ ///
+ int IFastBitmapWithOffset.Left {
+ get {
+ return _left;
+ }
+ set {
+ _left = value;
+ }
+ }
+
+ private int _top;
+ ///
+ /// Return the top of the fastbitmap, this is also used as an offset
+ ///
+ public int Top {
+ get {
+ return 0;
+ }
+ set {
+ _top = value;
+ }
+ }
+
+ ///
+ /// Return the top of the fastbitmap, this is also used as an offset
+ ///
+ int IFastBitmapWithOffset.Top {
+ get {
+ return _top;
+ }
+ set {
+ _top = value;
+ }
+ }
+
+ ///
+ /// Return the right of the fastbitmap
+ ///
+ public int Right => Left + Width;
+
+ ///
+ /// Return the bottom of the fastbitmap
+ ///
+ public int Bottom => Top + Height;
+
+ ///
+ /// Returns the underlying bitmap, unlocks it and prevents that it will be disposed
+ ///
+ public Bitmap UnlockAndReturnBitmap() {
+ if (BitsLocked) {
+ Unlock();
+ }
+ NeedsDispose = false;
+ return Bitmap;
+ }
+
+ public virtual bool HasAlphaChannel => false;
+
+ ///
+ /// Destructor
+ ///
+ ~FastBitmap() {
+ Dispose(false);
+ }
+
+ ///
+ /// The public accessible Dispose
+ /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
+ ///
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ // The bulk of the clean-up code is implemented in Dispose(bool)
+
+ ///
+ /// This Dispose is called from the Dispose and the Destructor.
+ /// When disposing==true all non-managed resources should be freed too!
+ ///
+ ///
+ protected virtual void Dispose(bool disposing) {
+ Unlock();
+ if (disposing) {
+ if (Bitmap != null && NeedsDispose) {
+ Bitmap.Dispose();
+ }
+ }
+ Bitmap = null;
+ BmData = null;
+ Pointer = null;
+ }
+
+ ///
+ /// Lock the bitmap so we have direct access to the memory
+ ///
+ public void Lock() {
+ if (Width <= 0 || Height <= 0 || BitsLocked)
+ {
+ return;
+ }
+ BmData = Bitmap.LockBits(Area, ImageLockMode.ReadWrite, Bitmap.PixelFormat);
+ BitsLocked = true;
+
+ IntPtr scan0 = BmData.Scan0;
+ Pointer = (byte*)(void*)scan0;
+ Stride = BmData.Stride;
+ }
+
+ ///
+ /// Unlock the System Memory
+ ///
+ public void Unlock() {
+ if (BitsLocked) {
+ Bitmap.UnlockBits(BmData);
+ BitsLocked = false;
+ }
+ }
+
+ ///
+ /// Draw the stored bitmap to the destionation bitmap at the supplied point
+ ///
+ ///
+ ///
+ public void DrawTo(Graphics graphics, Point destination) {
+ DrawTo(graphics, new Rectangle(destination, Area.Size));
+ }
+
+ ///
+ /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
+ /// Be aware that the stored bitmap will be resized to the specified rectangle!!
+ ///
+ ///
+ ///
+ public void DrawTo(Graphics graphics, Rectangle destinationRect) {
+ // Make sure this.bitmap is unlocked, if it was locked
+ bool isLocked = BitsLocked;
+ if (isLocked) {
+ Unlock();
+ }
+
+ graphics.DrawImage(Bitmap, destinationRect, Area, GraphicsUnit.Pixel);
+ }
+
+ ///
+ /// returns true if x & y are inside the FastBitmap
+ ///
+ ///
+ ///
+ /// true if x & y are inside the FastBitmap
+ public bool Contains(int x, int y) {
+ return Area.Contains(x - Left, y - Top);
+ }
+
+ public abstract Color GetColorAt(int x, int y);
+ public abstract void SetColorAt(int x, int y, Color color);
+ public abstract void GetColorAt(int x, int y, byte[] color);
+ public abstract void SetColorAt(int x, int y, byte[] color);
+
+ bool IFastBitmapWithClip.Contains(int x, int y) {
+ bool contains = Clip.Contains(x, y);
+ if (InvertClip) {
+ return !contains;
+ } else {
+ return contains;
+ }
+ }
+
+ void IFastBitmapWithClip.SetColorAt(int x, int y, byte[] color) {
+ bool contains = Clip.Contains(x, y);
+ if ((InvertClip && contains) || (!InvertClip && !contains)) {
+ return;
+ }
+ SetColorAt(x, y, color);
+ }
+
+ void IFastBitmapWithClip.SetColorAt(int x, int y, Color color) {
+ bool contains = Clip.Contains(x, y);
+ if ((InvertClip && contains) || (!InvertClip && !contains)) {
+ return;
+ }
+ SetColorAt(x, y, color);
+ }
+
+ ///
+ /// returns true if x & y are inside the FastBitmap
+ ///
+ ///
+ ///
+ /// true if x & y are inside the FastBitmap
+ bool IFastBitmapWithOffset.Contains(int x, int y) {
+ return Area.Contains(x - Left, y - Top);
+ }
+
+ Color IFastBitmapWithOffset.GetColorAt(int x, int y) {
+ x -= _left;
+ y -= _top;
+ return GetColorAt(x, y);
+ }
+ void IFastBitmapWithOffset.GetColorAt(int x, int y, byte[] color) {
+ x -= _left;
+ y -= _top;
+ GetColorAt(x, y, color);
+ }
+
+ void IFastBitmapWithOffset.SetColorAt(int x, int y, byte[] color) {
+ x -= _left;
+ y -= _top;
+ SetColorAt(x, y, color);
+ }
+
+ void IFastBitmapWithOffset.SetColorAt(int x, int y, Color color) {
+ x -= _left;
+ y -= _top;
+ SetColorAt(x, y, color);
+ }
+ }
+
+ ///
+ /// This is the implementation of the FastBitmat for the 8BPP pixelformat
+ ///
+ public unsafe class FastChunkyBitmap : FastBitmap {
+ // Used for indexed images
+ private readonly Color[] _colorEntries;
+ private readonly Dictionary _colorCache = new Dictionary();
+
+ public FastChunkyBitmap(Bitmap source, Rectangle area) : base(source, area) {
+ _colorEntries = Bitmap.Palette.Entries;
+ }
+
+ ///
+ /// Get the color from the specified location
+ ///
+ ///
+ ///
+ /// Color
+ public override Color GetColorAt(int x, int y) {
+ int offset = x + (y * Stride);
+ byte colorIndex = Pointer[offset];
+ return _colorEntries[colorIndex];
+ }
+
+ ///
+ /// Get the color from the specified location into the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference
+ public override void GetColorAt(int x, int y, byte[] color) {
+ throw new NotImplementedException("No performance gain!");
+ }
+
+ ///
+ /// Set the color at the specified location from the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference
+ public override void SetColorAt(int x, int y, byte[] color) {
+ throw new NotImplementedException("No performance gain!");
+ }
+
+ ///
+ /// Get the color-index from the specified location
+ ///
+ ///
+ ///
+ /// byte with index
+ public byte GetColorIndexAt(int x, int y) {
+ int offset = x + (y * Stride);
+ return Pointer[offset];
+ }
+
+ ///
+ /// Set the color-index at the specified location
+ ///
+ ///
+ ///
+ ///
+ public void SetColorIndexAt(int x, int y, byte colorIndex) {
+ int offset = x + (y * Stride);
+ Pointer[offset] = colorIndex;
+ }
+
+ ///
+ /// Set the supplied color at the specified location.
+ /// Throws an ArgumentException if the color is not in the palette
+ ///
+ ///
+ ///
+ /// Color to set
+ public override void SetColorAt(int x, int y, Color color) {
+ int offset = x + (y * Stride);
+ if (!_colorCache.TryGetValue(color, out var colorIndex)) {
+ bool foundColor = false;
+ for (colorIndex = 0; colorIndex < _colorEntries.Length; colorIndex++) {
+ if (color == _colorEntries[colorIndex]) {
+ _colorCache.Add(color, colorIndex);
+ foundColor = true;
+ break;
+ }
+ }
+ if (!foundColor) {
+ throw new ArgumentException("No such color!");
+ }
+ }
+ Pointer[offset] = colorIndex;
+ }
+ }
+
+ ///
+ /// This is the implementation of the IFastBitmap for 24 bit images (no Alpha)
+ ///
+ public unsafe class Fast24RgbBitmap : FastBitmap {
+
+ public Fast24RgbBitmap(Bitmap source, Rectangle area) : base(source, area) {
+ }
+
+ ///
+ /// Retrieve the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ /// X coordinate
+ /// Y Coordinate
+ /// Color
+ public override Color GetColorAt(int x, int y) {
+ int offset = (x * 3) + (y * Stride);
+ return Color.FromArgb(255, Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset], Pointer[PixelformatIndexB + offset]);
+ }
+
+ ///
+ /// Set the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ ///
+ ///
+ ///
+ public override void SetColorAt(int x, int y, Color color) {
+ int offset = (x * 3) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color.R;
+ Pointer[PixelformatIndexG + offset] = color.G;
+ Pointer[PixelformatIndexB + offset] = color.B;
+ }
+
+ ///
+ /// Get the color from the specified location into the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b)
+ public override void GetColorAt(int x, int y, byte[] color) {
+ int offset = (x * 3) + (y * Stride);
+ color[PixelformatIndexR] = Pointer[PixelformatIndexR + offset];
+ color[PixelformatIndexG] = Pointer[PixelformatIndexG + offset];
+ color[PixelformatIndexB] = Pointer[PixelformatIndexB + offset];
+ }
+
+ ///
+ /// Set the color at the specified location from the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b)
+ public override void SetColorAt(int x, int y, byte[] color) {
+ int offset = (x * 3) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color[PixelformatIndexR];
+ Pointer[PixelformatIndexG + offset] = color[PixelformatIndexG];
+ Pointer[PixelformatIndexB + offset] = color[PixelformatIndexB];
+ }
+
+ }
+
+ ///
+ /// This is the implementation of the IFastBitmap for 32 bit images (no Alpha)
+ ///
+ public unsafe class Fast32RgbBitmap : FastBitmap {
+ public Fast32RgbBitmap(Bitmap source, Rectangle area) : base(source, area) {
+
+ }
+
+ ///
+ /// Retrieve the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ /// X coordinate
+ /// Y Coordinate
+ /// Color
+ public override Color GetColorAt(int x, int y) {
+ int offset = (x * 4) + (y * Stride);
+ return Color.FromArgb(255, Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset], Pointer[PixelformatIndexB + offset]);
+ }
+
+ ///
+ /// Set the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ ///
+ ///
+ ///
+ public override void SetColorAt(int x, int y, Color color) {
+ int offset = (x * 4) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color.R;
+ Pointer[PixelformatIndexG + offset] = color.G;
+ Pointer[PixelformatIndexB + offset] = color.B;
+ }
+
+ ///
+ /// Get the color from the specified location into the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (a,r,g,b)
+ public override void GetColorAt(int x, int y, byte[] color) {
+ int offset = (x * 4) + (y * Stride);
+ color[ColorIndexR] = Pointer[PixelformatIndexR + offset];
+ color[ColorIndexG] = Pointer[PixelformatIndexG + offset];
+ color[ColorIndexB] = Pointer[PixelformatIndexB + offset];
+ }
+
+ ///
+ /// Set the color at the specified location from the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b)
+ public override void SetColorAt(int x, int y, byte[] color) {
+ int offset = (x * 4) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color[ColorIndexR]; // R
+ Pointer[PixelformatIndexG + offset] = color[ColorIndexG];
+ Pointer[PixelformatIndexB + offset] = color[ColorIndexB];
+ }
+ }
+
+ ///
+ /// This is the implementation of the IFastBitmap for 32 bit images with Alpha
+ ///
+ public unsafe class Fast32ArgbBitmap : FastBitmap, IFastBitmapWithBlend {
+ public override bool HasAlphaChannel => true;
+
+ public Color BackgroundBlendColor {
+ get;
+ set;
+ }
+ public Fast32ArgbBitmap(Bitmap source, Rectangle area) : base(source, area) {
+ BackgroundBlendColor = Color.White;
+ }
+
+ ///
+ /// Retrieve the color at location x,y
+ ///
+ /// X coordinate
+ /// Y Coordinate
+ /// Color
+ public override Color GetColorAt(int x, int y) {
+ int offset = (x * 4) + (y * Stride);
+ return Color.FromArgb(Pointer[PixelformatIndexA + offset], Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset], Pointer[PixelformatIndexB + offset]);
+ }
+
+ ///
+ /// Set the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ ///
+ ///
+ ///
+ public override void SetColorAt(int x, int y, Color color) {
+ int offset = (x * 4) + (y * Stride);
+ Pointer[PixelformatIndexA + offset] = color.A;
+ Pointer[PixelformatIndexR + offset] = color.R;
+ Pointer[PixelformatIndexG + offset] = color.G;
+ Pointer[PixelformatIndexB + offset] = color.B;
+ }
+
+ ///
+ /// Get the color from the specified location into the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b,a)
+ public override void GetColorAt(int x, int y, byte[] color) {
+ int offset = (x * 4) + (y * Stride);
+ color[ColorIndexR] = Pointer[PixelformatIndexR + offset];
+ color[ColorIndexG] = Pointer[PixelformatIndexG + offset];
+ color[ColorIndexB] = Pointer[PixelformatIndexB + offset];
+ color[ColorIndexA] = Pointer[PixelformatIndexA + offset];
+ }
+
+ ///
+ /// Set the color at the specified location from the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b,a)
+ public override void SetColorAt(int x, int y, byte[] color) {
+ int offset = (x * 4) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color[ColorIndexR]; // R
+ Pointer[PixelformatIndexG + offset] = color[ColorIndexG];
+ Pointer[PixelformatIndexB + offset] = color[ColorIndexB];
+ Pointer[PixelformatIndexA + offset] = color[ColorIndexA];
+ }
+
+ ///
+ /// Retrieve the color, without alpha (is blended), at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ /// X coordinate
+ /// Y Coordinate
+ /// Color
+ public Color GetBlendedColorAt(int x, int y) {
+ int offset = (x * 4) + (y * Stride);
+ int a = Pointer[PixelformatIndexA + offset];
+ int red = Pointer[PixelformatIndexR + offset];
+ int green = Pointer[PixelformatIndexG + offset];
+ int blue = Pointer[PixelformatIndexB + offset];
+
+ if (a < 255) {
+ // As the request is to get without alpha, we blend.
+ int rem = 255 - a;
+ red = (red * a + BackgroundBlendColor.R * rem) / 255;
+ green = (green * a + BackgroundBlendColor.G * rem) / 255;
+ blue = (blue * a + BackgroundBlendColor.B * rem) / 255;
+ }
+ return Color.FromArgb(255, red, green, blue);
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/FilenameHelper.cs b/GreenshotPlugin/Core/FilenameHelper.cs
new file mode 100644
index 000000000..47987163f
--- /dev/null
+++ b/GreenshotPlugin/Core/FilenameHelper.cs
@@ -0,0 +1,552 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using log4net;
+using System.Collections.Generic;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+
+namespace GreenshotPlugin.Core {
+ public static class FilenameHelper {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(FilenameHelper));
+ // Specify the regular expression for the filename formatting:
+ // Starting with ${
+ // than the varname, which ends with a : or }
+ // If a parameters needs to be supplied, than a ":" should follow the name... everything from the : until the } is considered to be part of the parameters.
+ // The parameter format is a single alpha followed by the value belonging to the parameter, e.g. :
+ // ${capturetime:d"yyyy-MM-dd HH_mm_ss"}
+ private static readonly Regex VarRegexp = new Regex(@"\${(?[^:}]+)[:]?(?[^}]*)}", RegexOptions.Compiled);
+ private static readonly Regex CmdVarRegexp = new Regex(@"%(?[^%]+)%", RegexOptions.Compiled);
+
+ private static readonly Regex SplitRegexp = new Regex(";(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", RegexOptions.Compiled);
+ private const int MaxTitleLength = 80;
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ private const string UnsafeReplacement = "_";
+
+ ///
+ /// Remove invalid characters from the fully qualified filename
+ ///
+ /// string with the full path to a file
+ /// string with the full path to a file, without invalid characters
+ public static string MakeFqFilenameSafe(string fullPath) {
+ string path = MakePathSafe(Path.GetDirectoryName(fullPath));
+ string filename = MakeFilenameSafe(Path.GetFileName(fullPath));
+ // Make the fullpath again and return
+ return Path.Combine(path, filename);
+ }
+
+ ///
+ /// Remove invalid characters from the filename
+ ///
+ /// string with the full path to a file
+ /// string with the full path to a file, without invalid characters
+ public static string MakeFilenameSafe(string filename) {
+ // Make the filename save!
+ if (filename != null) {
+ foreach (char disallowed in Path.GetInvalidFileNameChars()) {
+ filename = filename.Replace(disallowed.ToString(), UnsafeReplacement);
+ }
+ }
+ return filename;
+ }
+
+ ///
+ /// Remove invalid characters from the path
+ ///
+ /// string with the full path to a file
+ /// string with the full path to a file, without invalid characters
+ public static string MakePathSafe(string path) {
+ // Make the path save!
+ if (path != null) {
+ foreach (char disallowed in Path.GetInvalidPathChars()) {
+ path = path.Replace(disallowed.ToString(), UnsafeReplacement);
+ }
+ }
+ return path;
+ }
+
+ public static string GetFilenameWithoutExtensionFromPattern(string pattern) {
+ return GetFilenameWithoutExtensionFromPattern(pattern, null);
+ }
+
+ public static string GetFilenameWithoutExtensionFromPattern(string pattern, ICaptureDetails captureDetails) {
+ return FillPattern(pattern, captureDetails, true);
+ }
+
+ public static string GetFilenameFromPattern(string pattern, OutputFormat imageFormat) {
+ return GetFilenameFromPattern(pattern, imageFormat, null);
+ }
+
+ public static string GetFilenameFromPattern(string pattern, OutputFormat imageFormat, ICaptureDetails captureDetails) {
+ return FillPattern(pattern, captureDetails, true) + "." + imageFormat.ToString().ToLower();
+ }
+
+ ///
+ /// Return a filename for the current image format (png,jpg etc) with the default file pattern
+ /// that is specified in the configuration
+ ///
+ /// A string with the format
+ ///
+ /// The filename which should be used to save the image
+ public static string GetFilename(OutputFormat format, ICaptureDetails captureDetails) {
+ string pattern = CoreConfig.OutputFileFilenamePattern;
+ if (string.IsNullOrEmpty(pattern?.Trim())) {
+ pattern = "greenshot ${capturetime}";
+ }
+ return GetFilenameFromPattern(pattern, format, captureDetails);
+ }
+
+
+ ///
+ /// This method will be called by the regexp.replace as a MatchEvaluator delegate!
+ /// Will delegate this to the MatchVarEvaluatorInternal and catch any exceptions
+ ///
+ /// What are we matching?
+ /// The detail, can be null
+ /// Variables from the process
+ /// Variables from the user
+ /// Variables from the machine
+ ///
+ /// string with the match replacement
+ private static string MatchVarEvaluator(Match match, ICaptureDetails captureDetails, IDictionary processVars, IDictionary userVars, IDictionary machineVars, bool filenameSafeMode) {
+ try {
+ return MatchVarEvaluatorInternal(match, captureDetails, processVars, userVars, machineVars, filenameSafeMode);
+ } catch (Exception e) {
+ Log.Error("Error in MatchVarEvaluatorInternal", e);
+ }
+ return string.Empty;
+ }
+
+ ///
+ /// This method will be called by the regexp.replace as a MatchEvaluator delegate!
+ ///
+ /// What are we matching?
+ /// The detail, can be null
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static string MatchVarEvaluatorInternal(Match match, ICaptureDetails captureDetails, IDictionary processVars, IDictionary userVars, IDictionary machineVars, bool filenameSafeMode) {
+ // some defaults
+ int padWidth = 0;
+ int startIndex = 0;
+ int endIndex = 0;
+ char padChar = ' ';
+ string dateFormat = "yyyy-MM-dd HH-mm-ss";
+ IDictionary replacements = new Dictionary();
+ string replaceValue = string.Empty;
+ string variable = match.Groups["variable"].Value;
+ string parameters = match.Groups["parameters"].Value;
+
+ if (parameters.Length > 0) {
+ string[] parms = SplitRegexp.Split(parameters);
+ foreach (string parameter in parms) {
+ switch (parameter.Substring(0, 1)) {
+ // Padding p[,pad-character]
+ case "p":
+ string[] padParams = parameter.Substring(1).Split(',');
+ try {
+ padWidth = int.Parse(padParams[0]);
+ }
+ catch
+ {
+ // ignored
+ }
+ if (padParams.Length > 1) {
+ padChar = padParams[1][0];
+ }
+ break;
+ // replace
+ // r,
+ case "r":
+ string[] replaceParameters = parameter.Substring(1).Split(',');
+ if (replaceParameters != null && replaceParameters.Length == 2) {
+ replacements.Add(replaceParameters[0], replaceParameters[1]);
+ }
+ break;
+ // Dateformat d
+ // Format can be anything that is used in C# date formatting
+ case "d":
+ dateFormat = parameter.Substring(1);
+ if (dateFormat.StartsWith("\"")) {
+ dateFormat = dateFormat.Substring(1);
+ }
+ if (dateFormat.EndsWith("\"")) {
+ dateFormat = dateFormat.Substring(0, dateFormat.Length - 1);
+ }
+ break;
+ // Substring:
+ // s[,length]
+ case "s":
+ string range = parameter.Substring(1);
+ string[] rangelist = range.Split(',');
+ if (rangelist.Length > 0) {
+ try {
+ startIndex = int.Parse(rangelist[0]);
+ } catch {
+ // Ignore
+ }
+ }
+ if (rangelist.Length > 1) {
+ try {
+ endIndex = int.Parse(rangelist[1]);
+ } catch {
+ // Ignore
+ }
+ }
+ break;
+ }
+ }
+ }
+ if (processVars != null && processVars.Contains(variable)) {
+ replaceValue = (string)processVars[variable];
+ if (filenameSafeMode) {
+ replaceValue = MakePathSafe(replaceValue);
+ }
+ } else if (userVars != null && userVars.Contains(variable)) {
+ replaceValue = (string)userVars[variable];
+ if (filenameSafeMode) {
+ replaceValue = MakePathSafe(replaceValue);
+ }
+ } else if (machineVars != null && machineVars.Contains(variable)) {
+ replaceValue = (string)machineVars[variable];
+ if (filenameSafeMode) {
+ replaceValue = MakePathSafe(replaceValue);
+ }
+ } else if (captureDetails?.MetaData != null && captureDetails.MetaData.ContainsKey(variable)) {
+ replaceValue = captureDetails.MetaData[variable];
+ if (filenameSafeMode) {
+ replaceValue = MakePathSafe(replaceValue);
+ }
+ } else {
+ // Handle other variables
+ // Default use "now" for the capture take´n
+ DateTime capturetime = DateTime.Now;
+ // Use default application name for title
+ string title = Application.ProductName;
+
+ // Check if we have capture details
+ if (captureDetails != null) {
+ capturetime = captureDetails.DateTime;
+ if (captureDetails.Title != null) {
+ title = captureDetails.Title;
+ if (title.Length > MaxTitleLength) {
+ title = title.Substring(0, MaxTitleLength);
+ }
+ }
+ }
+ switch (variable) {
+ case "domain":
+ replaceValue = Environment.UserDomainName;
+ break;
+ case "user":
+ replaceValue = Environment.UserName;
+ break;
+ case "hostname":
+ replaceValue = Environment.MachineName;
+ break;
+ case "YYYY":
+ if (padWidth == 0) {
+ padWidth = -4;
+ padChar = '0';
+ }
+ replaceValue = capturetime.Year.ToString();
+ break;
+ case "MM":
+ replaceValue = capturetime.Month.ToString();
+ if (padWidth == 0) {
+ padWidth = -2;
+ padChar = '0';
+ }
+ break;
+ case "DD":
+ replaceValue = capturetime.Day.ToString();
+ if (padWidth == 0) {
+ padWidth = -2;
+ padChar = '0';
+ }
+ break;
+ case "hh":
+ if (padWidth == 0) {
+ padWidth = -2;
+ padChar = '0';
+ }
+ replaceValue = capturetime.Hour.ToString();
+ break;
+ case "mm":
+ if (padWidth == 0) {
+ padWidth = -2;
+ padChar = '0';
+ }
+ replaceValue = capturetime.Minute.ToString();
+ break;
+ case "ss":
+ if (padWidth == 0) {
+ padWidth = -2;
+ padChar = '0';
+ }
+ replaceValue = capturetime.Second.ToString();
+ break;
+ case "now":
+ replaceValue = DateTime.Now.ToString(dateFormat);
+ if (filenameSafeMode) {
+ replaceValue = MakeFilenameSafe(replaceValue);
+ }
+ break;
+ case "capturetime":
+ replaceValue = capturetime.ToString(dateFormat);
+ if (filenameSafeMode) {
+ replaceValue = MakeFilenameSafe(replaceValue);
+ }
+ break;
+ case "NUM":
+ CoreConfig.OutputFileIncrementingNumber++;
+ IniConfig.Save();
+ replaceValue = CoreConfig.OutputFileIncrementingNumber.ToString();
+ if (padWidth == 0) {
+ padWidth = -6;
+ padChar = '0';
+ }
+
+ break;
+ case "title":
+ replaceValue = title;
+ if (filenameSafeMode) {
+ replaceValue = MakeFilenameSafe(replaceValue);
+ }
+ break;
+ case "MyPictures":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
+ break;
+ case "MyMusic":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
+ break;
+ case "MyDocuments":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+ break;
+ case "Personal":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
+ break;
+ case "Desktop":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ break;
+ case "ApplicationData":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ break;
+ case "LocalApplicationData":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ break;
+ }
+ }
+ // do padding
+ if (padWidth > 0) {
+ replaceValue = replaceValue.PadRight(padWidth, padChar);
+ } else if (padWidth < 0) {
+ replaceValue = replaceValue.PadLeft(-padWidth, padChar);
+ }
+
+ // do substring
+ if (startIndex != 0 || endIndex != 0) {
+ if (startIndex < 0) {
+ startIndex = replaceValue.Length + startIndex;
+ }
+ if (endIndex < 0) {
+ endIndex = replaceValue.Length + endIndex;
+ }
+ if (endIndex != 0) {
+ try {
+ replaceValue = replaceValue.Substring(startIndex, endIndex);
+ } catch {
+ // Ignore
+ }
+ } else {
+ try {
+ replaceValue = replaceValue.Substring(startIndex);
+ } catch {
+ // Ignore
+ }
+ }
+ }
+
+ // new for feature #697
+ if (replacements.Count > 0) {
+ foreach (string oldValue in replacements.Keys) {
+ replaceValue = replaceValue.Replace(oldValue, replacements[oldValue]);
+ }
+ }
+ return replaceValue;
+ }
+
+ ///
+ /// "Simply" fill the pattern with environment variables
+ ///
+ /// String with pattern %var%
+ /// true to make sure everything is filenamesafe
+ /// Filled string
+ public static string FillCmdVariables(string pattern, bool filenameSafeMode = true)
+ {
+ IDictionary processVars = null;
+ IDictionary userVars = null;
+ IDictionary machineVars = null;
+ try
+ {
+ processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
+ }
+
+ try
+ {
+ userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
+ }
+
+ try
+ {
+ machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
+ }
+
+ return CmdVarRegexp.Replace(pattern,
+ m => MatchVarEvaluator(m, null, processVars, userVars, machineVars, filenameSafeMode)
+ );
+ }
+
+ ///
+ /// "Simply" fill the pattern with environment variables
+ ///
+ /// String with pattern ${var}
+ /// true to make sure everything is filenamesafe
+ /// Filled string
+ public static string FillVariables(string pattern, bool filenameSafeMode) {
+ IDictionary processVars = null;
+ IDictionary userVars = null;
+ IDictionary machineVars = null;
+ try {
+ processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
+ } catch (Exception e) {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
+ }
+
+ try {
+ userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
+ } catch (Exception e) {
+ Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
+ }
+
+ try {
+ machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
+ } catch (Exception e) {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
+ }
+
+ return VarRegexp.Replace(pattern,
+ m => MatchVarEvaluator(m, null, processVars, userVars, machineVars, filenameSafeMode)
+ );
+ }
+
+ ///
+ /// Fill the pattern wit the supplied details
+ ///
+ /// Pattern
+ /// CaptureDetails, can be null
+ /// Should the result be made "filename" safe?
+ /// Filled pattern
+ public static string FillPattern(string pattern, ICaptureDetails captureDetails, bool filenameSafeMode) {
+ IDictionary processVars = null;
+ IDictionary userVars = null;
+ IDictionary machineVars = null;
+ try {
+ processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
+ } catch (Exception e) {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
+ }
+
+ try {
+ userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
+ } catch (Exception e) {
+ Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
+ }
+
+ try {
+ machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
+ } catch (Exception e) {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
+ }
+
+ try {
+ return VarRegexp.Replace(pattern,
+ m => MatchVarEvaluator(m, captureDetails, processVars, userVars, machineVars, filenameSafeMode)
+ );
+ } catch (Exception e) {
+ // adding additional data for bug tracking
+ if (captureDetails != null) {
+ e.Data.Add("title", captureDetails.Title);
+ }
+ e.Data.Add("pattern", pattern);
+ throw;
+ }
+ }
+
+ ///
+ /// Checks whether a directory name is valid in the current file system
+ ///
+ /// directory name (not path!)
+ /// true if directory name is valid
+ public static bool IsDirectoryNameValid(string directoryName) {
+ var forbiddenChars = Path.GetInvalidPathChars();
+ foreach (var forbiddenChar in forbiddenChars) {
+ if (directoryName == null || directoryName.Contains(forbiddenChar.ToString())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Checks whether a filename is valid in the current file system
+ ///
+ /// name of the file
+ /// true if filename is valid
+ public static bool IsFilenameValid(string filename) {
+ var forbiddenChars = Path.GetInvalidFileNameChars();
+ foreach (var forbiddenChar in forbiddenChars) {
+ if (filename == null || filename.Contains(forbiddenChar.ToString())) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/Greenshot.Base/Core/Fraction.cs b/GreenshotPlugin/Core/Fraction.cs
similarity index 97%
rename from src/Greenshot.Base/Core/Fraction.cs
rename to GreenshotPlugin/Core/Fraction.cs
index e7d999445..312b91bb8 100644
--- a/src/Greenshot.Base/Core/Fraction.cs
+++ b/GreenshotPlugin/Core/Fraction.cs
@@ -1,7 +1,7 @@
using System;
using System.Text.RegularExpressions;
-namespace Greenshot.Base.Core
+namespace GreenshotPlugin.Core
{
///
/// Basic Fraction (Rational) numbers with features only needed to represent scale factors.
@@ -19,12 +19,10 @@ namespace Greenshot.Base.Core
{
throw new ArgumentException("Can't divide by zero.", nameof(denominator));
}
-
if (numerator == 0)
{
throw new ArgumentException("Zero is not supported by this implementation.", nameof(numerator));
}
-
var gcd = GreatestCommonDivisor(numerator, denominator);
Numerator = numerator / gcd;
Denominator = denominator / gcd;
@@ -36,7 +34,6 @@ namespace Greenshot.Base.Core
#region Parse
private static readonly Regex PARSE_REGEX = new Regex(@"^([1-9][0-9]*)\/([1-9][0-9]*)$", RegexOptions.Compiled);
-
public static bool TryParse(string str, out Fraction result)
{
var match = PARSE_REGEX.Match(str);
@@ -45,7 +42,6 @@ namespace Greenshot.Base.Core
result = Identity;
return false;
}
-
var numerator = uint.Parse(match.Groups[1].Value);
var denominator = uint.Parse(match.Groups[2].Value);
result = new Fraction(numerator, denominator);
@@ -82,7 +78,7 @@ namespace Greenshot.Base.Core
}
public int CompareTo(Fraction other)
- => (int) (Numerator * other.Denominator) - (int) (other.Numerator * Denominator);
+ => (int)(Numerator * other.Denominator) - (int)(other.Numerator * Denominator);
#endregion
@@ -153,4 +149,4 @@ namespace Greenshot.Base.Core
private static uint GreatestCommonDivisor(uint a, uint b)
=> (b != 0) ? GreatestCommonDivisor(b, a % b) : a;
}
-}
\ No newline at end of file
+}
diff --git a/GreenshotPlugin/Core/GreenshotResources.cs b/GreenshotPlugin/Core/GreenshotResources.cs
new file mode 100644
index 000000000..8f59dee91
--- /dev/null
+++ b/GreenshotPlugin/Core/GreenshotResources.cs
@@ -0,0 +1,43 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System.ComponentModel;
+using System.Drawing;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Centralized storage of the icons & bitmaps
+ ///
+ public static class GreenshotResources {
+ private static readonly ComponentResourceManager GreenshotResourceManager = new ComponentResourceManager(typeof(GreenshotResources));
+
+ public static Image GetImage(string imageName) {
+ return (Image)GreenshotResourceManager.GetObject(imageName);
+ }
+ public static Icon GetIcon(string imageName) {
+ return (Icon)GreenshotResourceManager.GetObject(imageName);
+ }
+
+ public static Icon GetGreenshotIcon() {
+ return GetIcon("Greenshot.Icon");
+ }
+
+ }
+}
diff --git a/src/Greenshot.Base/Core/GreenshotResources.resx b/GreenshotPlugin/Core/GreenshotResources.resx
similarity index 98%
rename from src/Greenshot.Base/Core/GreenshotResources.resx
rename to GreenshotPlugin/Core/GreenshotResources.resx
index ea1760d58..521a6125f 100644
--- a/src/Greenshot.Base/Core/GreenshotResources.resx
+++ b/GreenshotPlugin/Core/GreenshotResources.resx
@@ -1,471 +1,471 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
-
-
- 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
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSkpKSisCAAAAAAAAAQAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFTg9JgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAgAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
- AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
- AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
- AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
- AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
- AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
- AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCKAAAACAAAABA
- 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
- YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAwBQTFRFgICA////
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODgHVgAAAAlwSFlzAAAOvgAA
- Dr4B6kKxwAAAABZJREFUGFdjYAABRhAAs4hlkq4DZDgACywAM12jTsYAAAAASUVORK5CYII=
-
-
-
-
- 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
- mXizAAAAAElFTkSuQmCC
-
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAXJJREFUOE+lk0FL
- AkEYhlvwv3jzoiDoQdCbdEnYf6CrqCgoHgRRAk/9EQVLdEGyFiQNMS+dvHnoEkgglGAmCL7NO6RMIZvU
- wsMO3zzzzGk0ACf/+hjQNO1ccKlXKsYx0OUZeflXoFmtVsUS2P4CHboi0FQDrXK5jM12i/VmYwsduiLQ
- UgNmqVTCuzj8tlrZQoeuCJhqoFMsFvG6XmO2WNhCh64IdNRAt1Ao4EXc/jSf20KHrgh01YCVy+Uwnkzw
- vFzaQoeuCFhqoJfJZBCLxY6Crgj01EA/lUrB4/HA7XYfhHs78vk8A301MIzH4/B6vRiNHjAY3H+DM+7p
- ug6fz4dsNsvAUA2Mo9Eo/H4/LOsOTqdTYprXEs64x0AwGEQ6nWZgrAYeDcNAIBBAu30r/6Reb0t2MwbC
- 4TCSySQDj/uAeEyngqnL5fpoNG4QCoUktVpHspsxEIlEkEgk+AKnaoAP8kwwczgcF4fg3g+u9gEu/son
- bfJW/NwRDyIAAAAASUVORK5CYII=
-
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAUNJREFUOE+lk79L
- QlEcxW9/gqCrm6vg4uYoOAgOrqLk4ioP0r2Glhp0SSjoF1FE0BIUDU3RdIOGoKBVGlpapaHTObeuCPe6
- 9ITD5fs9n3Pue8JbAWBS/VSQRvPwKR/j3JgaZXVqPv5TzPOXLhYoZDEcQidVWyhw3qzfn3tBAWH7PRjg
- uV7HV5JAM6USyX50u86btlrOCwoOCR7Q+Oz1cFcu473dhmbppdFwu8dq1e3EBgU0zB6NXQJvzSaui0U8
- VCq4LZWwn8vhLJ+HPDFiowUEzITADsGrQgFHmYzTSTYL7eSJiRZs0timRoTGhC956wXDXtrJEyM2eAIt
- t34Be8NgTPLELCuQYe8Z9tK8ZBf+ieuEnxj20rzB26SYF7zCGsGEoVeW6NTMoJFiXlDAkFllqMOwTs2+
- IOYFBf/9oFJ9ibr0B4f94vVG3bWDAAAAAElFTkSuQmCC
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ AAABAAUAAAAAAAEACAClFwAAVgAAADAwAAABAAgAqA4AAPsXAAAgIAAAAQAIAKgIAACjJgAAGBgAAAEA
+ CADIBgAASy8AABAQAAABAAgAaAUAABM2AACJUE5HDQoaCgAAAA1JSERSAAABAAAAAQAIBgAAAFxyqGYA
+ ABdsSURBVHja7Z1fqFVVHsf3YQqnUTJQSJMcujkK3UHuFW5geBXGYK5B0EP6Gto8zIsG8zKY82rCvKXP
+ 6bv2FqQP9eAfEhS8Eilozo0xTAOFbGycKLjTd9u6nnvvXnuvvff6/dbea30/cEioPPucs9Z3/dbv72By
+ cnI2I4QkyYACQEi6UAAISRgKACEJQwEgJGEoAIQkDAWAkIShABCSMBQAQhKGAkBIwlAACEkYCgAhCUMB
+ ICRhKACEJAwFgJCEoQAQkjAUAEIShgJASMJQAAhJGAoAIQlDASAkYSgAhCQMBYCQhKEAEJIwFABCEoYC
+ QEjCUAAISRgKACEJQwEgJGEoAIQkDAWAkIShABCSMBQAQhKGAkBIwlAACEkYCgAhCUMBICRhKACEJAwF
+ gJCEoQAQkjAUAEIShgJASMJQAAhJmOgF4MllP2dP/+GH/M8rx77L7t9Ylv304Ins4e0l2X/v/Db04xES
+ lCgF4Her/pc9v+PbbNXkvezpdT9Y/7uHd5Zkt8+tzL4++Wz2/ZdLQz82IepEJQDY+Ov33Myen/q29v97
+ 7/Ly7Nqx32f3ppeH/hiEqBGNAIzsvJVv/ieX/tzq75n5cE12/eja/JpASOxEIQBj715vdOrb+P7G0uyz
+ fRspAiR6ei8Avje/gSJAUqDXArBh97+z9btviv398AtABAiJld4KwIrx+9kr738u/j5XjoxkMyfWhP64
+ hIjQWwF45fDn2Yqx++Lv89MPT2Sf7pzgVYBESS8FQOv0N1w/tjYPERISG70UgIn3rmarttxTez9YAad2
+ bA79sQnxTu8EAKm9Ux+fV3/fiwdeyu6cXRH64xPild4JANJ7Jw5eVX9fJAhdOTwS+uMT4pXeCYB06M9G
+ m5AgfBYoRDJ/BihK+vk/v8nuXn6G6cckGL0TAO37vwGFQ5/setn5v0cFItKTYbFUpSfDx4DrBYqSKAZE
+ k94JgFb4r4iPtk5W/jcoSBrdN9NYpGBpfHHkRVYnEhUoADWoEgCUIGPzty1IAkxAIhr0TgBCXQFQG3B6
+ zybrv8fGH3nzltf3/PrUs9nl99arf1aSDr0TgC46ASWfiSJAJOmdAIQKA9qyATWyEi8fWp87CAnxTe8E
+ IFQi0Om3Ny1yzOFZth29lD216kfR92Y9ApHCSQDg2cZJh38ivIWFj4aaprEmQleaDTalegDYsIUANa8j
+ vAoQCawCgE0OrzZi2S4nHJxk8Fojni19UnWhGAjfz/YTF714/F35dNcEOxkTrxQKAE62F3Z902hxw1xF
+ Tz3pEFbocmCI49j+6+LvPwxDg8Q38wQAJj7CbGWttF2B1/ziuy+JWQN41q3HpsVPYFsRUIhwZFUokpC6
+ zAkA7vY4VX1uKNydLxwYFctqkz6Fy+7dUyfPq5r/hlOvbaYzkHgjFwCJzW+ACODUklq0kk1BbactrI/t
+ xy+KfJ4qPntnY+16ATxvPiTll985d+gOXZ1gqRlHrrYzl4Rn8Kcdm2ex+X2Y/Takm2v6zsK7c25FfvLb
+ REvbCTlMHQHAc+YFSTWuKvjs8DOwKCkNBn89sWbWdwprEdIOLJxwsAbaWDGuDsyQAuDyPeKUx3fRxkkK
+ 0YYI0iKIm8E/ZzOVRCCNZBaE5nDiNYlg4L6Pze+y4LtsAfgQQgN+M4gAOyHFi5oAAK3mmhACbAS8sFlt
+ mwGnHBY3XnVOOtylt31wSetrm0eZAEg5RZmKHC+qAlC3qYYvYBI/tfpxMhOskLaRidfPnFX/HMCWDCRd
+ I9HE+Ui6j6oAgKKc+j6CGgBJx2kRNgHVyEpkPUKcqAtALNls8DWM7p1RfU9bY1KtpCTWI8SHugA0XUTm
+ Pr983YNHBUm/nnaI1+NUgnl6+9xKNesiRC5AkfWk7ZCMxYIjj1AXgDo5Adhk8OjDueVq3sJMhoUBp5W0
+ uapZlWj73rQrI2kFxEVnBaBNQRKAEKC5pmQIS9MKKHLCheqNwHTkeOicAGBR407rq9JP+sTS6Algu/uH
+ 6o7EKUnxoC4ASDVFlWAR2PwSacnSIiBZmgwfBwSz6MQN1R/RRz6HaSwDTGMZoo+6ANgWj9TmN0iKgNSz
+ l21+EKpFepPaDmx4+HIwIcn2PeHvxTUH/hsKgg7qAmBLKNEIZUmGICECcMj5+gwu/RT6IACIUvxx779q
+ iyPeAwcFk49kURUA25htrVCWRjIL8gPW77nZ2HmJZ/zq+HNOJnaXBcCXLweWG/wfdDrKoCoANjNccyFr
+ hLGaFCVh48P0xeZ3NX+7KgC++0vgKjR9aAPzDwRQFYCiPPYQlXWaYSxbAhMwzThMQVJdQglAmSNXqrkM
+ BBKiQxHwy+Dv08tnNRaR7eTVTmQBsVS3dS0KIN2nscopSuoz+PPOiVnp5ppld+8QvfXKTrA+Eaovgc2R
+ q2GRxPLbdYW8J6B0c03bgglVV29zRvYRbQG1fXeabdJZmuyPua7AIZpJhOysE0s6q8RU4jJsWYmvHr8g
+ PiLNIN1jMiXmzQXwOd/epZ1UqDssiOUU0a5KLHLkhkhJZlWiHxZNBoJZjsQNjYaSFAA/aH2PNudfiCEp
+ NkuE1MM6GxDWAF51hKBu9laIphqGmARAOo0alM1JCOHI5ZQkP1ROBzZDJeamAw8tMvwIZqhE3caaIKQP
+ 4KOtk0HeVwrJ4S5lMfiQDVLb/IZmPeNluH9jWb6GU7paOI0HlyLUhJ1QzUmlkRCBqgScLrdIXwgOMli1
+ VdcVfGYcaKgbiV0MggoA0PQeG2LuauNzwCssPMTcyyy7EFOSDa4CgI0Pv1aTdYZrLRrLxCoEgzf2bcwF
+ IFRNtnYYC6TQ0KLtiHfXgqQu+3F8VmhqzbTQZlEtQNNhGU3RvgbElARUhSlKwintcvrhaoScDZi+rjkS
+ Xb0C4Do0vv+aV8eo9Mj7EJQWA9UZl9UGzXqAWJW8CmwINOPAgBQUJhng+IL1d/fyM43M3C4mc0nWJMSW
+ hORUDSi9abSsAOlR5akSYkpSmSNXemhLTDkIzuXA0uaPRjJLTLH/LhGiLNnmyNVKioplLdXqByBdjil5
+ FYilBLiLhIgEFG1ATX9SLKHk2g1BpMsxJUQg1Xu/FhqzCYexbT7t3hIxHCqNOgJJz/fzFRqExx93tb7/
+ SH1As67DtvG0U5JjcAg2EgCN5ppNu8kaYKlg87O9tA6wAuB8k07qsm26UENS+l5W3rgnoJYn1DV9E6SU
+ wtlFpEOC+H3P7B4vFPUQCWWg70lljQVAe148Tph5zTV/nSqDxWDi2DF4ZfuOpEOwbLOFapDad/9Sq67A
+ MThBiH98i4BLc5kQJcmg7z0KWwlAzEU1pB2w1pCK29Yn4DoTIEQyEui7I7CVALRpygAT3qSnDoPUVPzY
+ dN71nyYDUgx1CpIABaAZrQeD1GnKgAUBpx4WRZV3H7He2+dW1pqUQ7oJfvfckYvGMhX3dJjUMPXrXi1D
+ lJWDvlvBagLQpjxVqyiJ6LCwGw+sPjiT2zhx6QRshrgA+CrLZFIPKSNUg9m+1wSICgCUHt1pfHpn+25y
+ ERlC9CaMobdEKwEoK4iQ/EEoAqQIbT9ADOuwlQDYYqDSQyIBcxDIQrSrEouGpPQNkUQgDYdMWVooSRct
+ KyCG0x+0EoCiQgjNFlGx/AjEHxrrL6bDp7EA2DafdjgmBjOM+EW6MKjvBUDDNBaAoo0XwhMbU3824g+p
+ 5iCx+Z4aCYBt04UoyYylNRPxj28RiG3zg9oCUNYXMFQ2Fq8BxAbSjyEEbSJSrgVJfaSWAFQ5P0IVZMR0
+ JyP+aVqUBOsSab6xnfrDOAsAvowLB0atKogveerj80E+RN/zsYke+cj78fuPhqQUhAtx2qM2wUzHih0n
+ AUDCDzz+Zd1/Qk6IoQCQpiBpDdOSQs3GDE2pAODUx2RUFyWkABDSPxYJgGms2cQECuUDkG5TTkisDP5y
+ dG0uAGiqCRO/jaczVF+2vpdkEhKKweTkZKty4GFQ+utjFntd6nQlIoQ8xqsAINQyundG9QP0vSsrISHx
+ KgAhQoExZmcRooVXAQCaAxq1h5MQEhveBUBzRDPDf4S0w7sAAI0GjW1mEhBCHiEiAECyMAimPwqSYizO
+ IEQTMQGAQxAi0LYd+EK4+Qnxx+CtwyOzUll0EAHkBviyBLj5CfHL4OCDJ2al+5v58Am4FCQRQuqR1wJo
+ NNdEdGD9npu1Q4QYvghPP1N9CfHPXDGQVlcdMyA0HxQ5fr+wdgCbHjXZSPChuU+IHHMCELKiztRk85Qn
+ RJc5AWBcnZD0mNcPgFV1hKTFPAFoUlePWQDos7Z83YNF4T6E7XCHx995+9xK3ucJ6RiNBQCbHuG9OnPY
+ cM2An4HVe4R0g9oCAM/9+P5rrQYwwsuPXoO0CAgJSy0B8NnwA9cDTBeiNUBIOJwFgLPWCIkPpyiAdKsv
+ TvYhJAyVeQAaE39jmrdOSJ+ozATUGvjJ5p6E6FNaC4B8/YmDV9Ue5vTbmxgZIESR0mpA7XHfGlWJhJDH
+ 5AJQdPprNvcchunIhOiRjwYr6qyLTL+x/dfVH4gRAUL0sPYE1OzvPwxbfROih1UAtO//BkYDCNHDKgDb
+ jl7y3tHXBdQJoPEnIUQeqwC8fuZskAeiABCiR+euABQAQvTonADMfLgmrxIkhMhjFQCN+X5FhGxOSkhq
+ WAVAOw3YoNWenBBSMRtw6uT5wr79UrAzMSG6lArA6L6ZbOTNW2oPQ/OfEF1KBQD1AFuPTatYAegJ8OnO
+ Cc7+I0SRyvHgWs5AtgYjRJ9KAQDSWYFM/yUkDE4CIHkVgOMPiT80/QnRx0kAAHoDIjnIpwhw8xMSFmcB
+ ABjtPfHeVS8ZgjD70f2Hm5+QcNQSAEOTsWCGh3eW5FOB2PSDkPA0EgADhAAvF4sAJz42PT39hHSHVgJg
+ wNUAPoKVY98t+nd3Lz+Td/qlqU9I9/AiAISQfkIBICRhFglAmTmPKj0MD2W1HiFxMCcAKP+FQ2/VlnuV
+ /xM8+SjagUOPd3tC+svgjX0bZ8f3X2sU0kMBz1fHn8vFgEJASP+YNx68KbAILhwY5Vw/QnqGFwEAsAaQ
+ 2ccEH0L6gzcBABAB5PbTEggL/DnL1z3IVow/StBCohasNDhv8cLvA6GmM5d4FQDAxh5hQMXmC7u+yR25
+ rgVbaMEORy6zM9PFuwAA1vfrgroMbP6mlZqoypw+tIGWW4KICAD47J2Nec4AkQM5GyjR9tWshT0Z00NM
+ ADjhRxaJ/gzg61PP5s5ckgZiAgDY418GnPxo09Ykd8MFjmhPB1EBoEkpg8bYNl7h0kBUAOgM9A+8/GP7
+ r4u/D8KGn+x6OfTHJcKICgAXkX9ePX5BzPRfCFu1x4+oAICPtk6G/ozRoHX6Gyjg8UMB6BFoyOpSremT
+ iwdeYnp3xFAAegI8/1Mfn1d/35kP12RXDo+E/vhECApAT0Be/yvvf67+vr4mNpu6BPaH7BaiAsBkIH9o
+ zWgsoq6Iw1rJu0X/sunxKkpWgrCgYSycjExBDoeoANB89EcfBAAFSev33Myen/q21t8PMTAdpoguogJw
+ +u1NVHdPdF0AfDwfLEakITN7VA8xAWAIyS9dFQCf4+IAG8voIiYAzCf3y8jOW9no3pkg720TAN/ViMMw
+ CUkHEQHA6Q/PMb29/ggVBShz5ErWJLC7lA4iAsDkERleP3NW/T1tjlyNKwkPEnm8CwA9/3KEyAQscuTC
+ 27/12LT3XgRF8Copi1cBYDMJWdDsc+LgVbX3szlyx969XjvU1xT2mJTFmwDw5NchdDUgHH/bT1xUOf0N
+ 7Cshx+BvZ1fMtjErcUp8ceRF3vmV0HIG2lKAtSsSy56FtCefDYhFhTBTHSHgfMBwaJjgtiSuEH4IcOq1
+ zVxnAsybDgznDu6ZEAQMllhoaiIkdP/GsrxVFE/8cEjG30FZDF7zCjJMmxZlWNdPrf5x3sRrrGMzJCVl
+ Fo0HJ/1ASgSqEnBChCJB3WiAGZSyesvdUsGCkxGHGT5zij0QKQA9BiKA64APk9w1BbfrAoCNP7pvptF3
+ kqI/iwIQAXDMITGnqWmO5q2I4LgU4XRZAHylS+P7gBim4HMYvHV4ZJaDIvsPrAH4b7AJXK8FyNuAI7fO
+ PbirAuDbMYrIAzpax74v5vIA4ODDF5ziPSg2YAbDkYvpQXDmGnCiYbPDAdbUzA0lAGXp5VJRkRRSkRcl
+ AuFUgDkY84cmzdEYSlKEbcqUdE1C7DkIhZmAUL4LB0aTD5GQxYQoS7alJGslRcVcj2BNBWY5JikC14vt
+ xy+qvqctzVzTGol1zmVpLUAKdyBSH+1rQNHm0+6PEGuhW2UxEOf7kYVobj7bxtNOSY61KtGpGpCTYslC
+ NDYgNt2Z3eOLTv9QQ1JibHTjJADs708WolEWbEtLDtUeLcZrgHM/AFoBZCHIM4A/QEIEyjZbqA7JMR6E
+ zgIQo/qR9kiIQNVa0+xItJDYRt05CwD7/BMbCA3CJ9C2MhF3foT7qtqBh0pGAskKAGjTlAEnBRbKcGoq
+ ZsM9vL0kyvhqiiBJCKPBmlgDOPWvH13rtBYoAP6oJQB1/QBw1qBSDUUqZYsC1gXEoG5hCukm5jevihLg
+ d799bmX21fHnah0CKPcdefOW+ueK0QoWEQBsfDhqmqg0HC2oyaYQxAHWAiw/vAxtLT86Af3hVQAQGoIJ
+ 6EOd2WWY2AgVBoyxJsCbAEi0qILiIgsxtuwr0p6pk+dVW5ODGKdd1xIAW0GEZJPK2Msx+wKcuGiqieaa
+ w45c9BaAOQ+zXnNzaIcCY7z/A2cBQIjm1I7Nhf9O2ivLHIQwQNjh2YdTz6XdGDYJQnhw5kpbbdpViTGa
+ /8BZAGxFQVr14THmYXcZONrQVbeJmY3DAp596Q2jZQXEWggEnAUg9JioWE2wrgFTf3z/NS/XOVzfpg9t
+ ELsaaA0pjfnwcRIAm/mvHY6p6llP2iGR1ivdWEZ6YGrs108nAbDdf7SnxNAhKIdkYY+0CEjNK0xhvVUK
+ QOh+bAuJtTVTSKRHjQHpzeRbBFJpjlspALbYZ6hsLF4D/KOVWivtScehBP9FG6sU1gpqElIZR14qAGWb
+ LVRBRtMMQdMr3/zT4KNXfp/RDqdpWHA4nFxDl8PUKUiKBasAVJ20oQSgbj42FgJers+KRZDSoEjtwhrN
+ eHpekPTLK09gKhADnPZm0jVesZv7RSwSANeZACFSMYGrALQ1B/E+8P7Gfhpo/45lCWXSDFt+WN8pbviF
+ zAmASd5wzeIKNSfeRQB8nWquDSr6SihHLtvLdYfBP75cOosFjlcdReziFQDebHSm8f1csVYmhnLkxppW
+ 20cajwfX7stuKFs8ks905chIdJ7hUCLeZNaEmX4MqwXFSAtDljgY4Mg1d3riRmMBCDEjDtjSMjVOs9hM
+ 1y5acQtBlAI9Jqq6Sg1T9zqbMo0FAJlj2z64pP7ARX0JtZ4ltlFpXReAtqKeWky/CY0FAGw7ekk0e2wh
+ NtNRcyHHdH/tqgD4zkzEukFEJxbh9kkrAZDKwbZRZIJrJ7KEDGP5posCIJWWjFRkvCdFYD6tBABohQNt
+ iyZEh9hYykNDdde1WVHSNQkxNvVsS2sB0Iol22oSQuQjxFIiKl1Ka8MmoBqRpZiucD5oLQBA+iSxpSVr
+ m/+GWJqThJiya7tCaSYlsaL0MV4EAEi1Zyo7bUNlsoEmE2IQrVi95W7+3PjzcFgLd1QsShPH1lqg2s01
+ bb+npj8iFgvOB94EAPheTFXmWqhMNlAnJwDOUjxrnasKPNcIX0nnHWiLaNHpG0LIaQU8wqsAANwrIQRt
+ CkxgYqOXXNXi77oA+KhP1whhaWV12k7eENN+Y8zsbIJ3AQCmnXTdrrJ1M7i6LAA+n026pZZGc9eyzroh
+ HLmMCDxCRAAMJn/b5HAXLTAzGNTUZNchlBcblE1KljjRpEVA2gy3CWYoRy6IbdJvE0QFoIjhmuy299tQ
+ 6chlyUCSVklfm2uWNZcJ6ciNcdRXXdQFwDchGpPYUpI1FrN0RpsPH44BgoXvqUzotbNJh4mtuKsJ/wfb
+ mhgAeoKg9wAAAABJRU5ErkJggigAAAAwAAAAYAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8
+ PDwAOkE+ADpEPwA5RUAAN01DADdORAA4SUEAOExDADVRRAA0VUYANFhHADNaSAA0WUgAMl1JAC9nTQAu
+ ak4ALWxPADFgSwAwY0wAMGRMAC1uUAAscVEAKnRSACp3VAApeVQAKH1WACeAVwAmg1gAJYVZACSIWgAk
+ i1wAIo1cACGSXgAhlF8AH5lhAB6cYgAdn2QAIJZgACCYYQAcomQAG6ZmABykZQAbqGcAGqpoABmtaQAX
+ smsAFrVsABixagAVuW4AFLxvABO/cAAUvnAADs52ABLAcQARx3MAEcd0ABDKdAAO0HcADdJ4AAzWeQAL
+ 2XoADNh6AAndfAAH5X8ACOJ+AAjkfwAH5oAABumBAATuggAD8oUABPCEAAL1hQAB+IcAAfqIAAD+iQBx
+ /50Akf+yALH/yQDR/98A////AAAAAAACLwAABFAAAAZwAAAIkAAACrAAAAvPAAAO8AAAIP8SAD3/MQBb
+ /1EAef9xAJj/kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAwcAAAPZAAAEywAABZzwAAZ/AAAHj/EQCK
+ /zEAnP9RAK7/cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABAUAAAWnAAAHSQAACOsAAAqc8AAMLwAADR
+ /xEA2P8xAN7/UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAvJgAAUEEAAHBbAACQdAAAsI4AAM+pAADw
+ wwAA/9IRAP/YMQD/3VEA/+RxAP/qkQD/8LEA//bRAP///wAAAAAALxQAAFAiAABwMAAAkD4AALBNAADP
+ WwAA8GkAAP95EQD/ijEA/51RAP+vcQD/wZEA/9KxAP/l0QD///8AAAAAAC8DAABQBAAAcAYAAJAJAACw
+ CgAAzwwAAPAOAAD/IBIA/z4xAP9cUQD/enEA/5eRAP+2sQD/1NEA////AAAAAAAvAA4AUAAXAHAAIQCQ
+ ACsAsAA2AM8AQADwAEkA/xFaAP8xcAD/UYYA/3GcAP+RsgD/scgA/9HfAP///wAAAAAALwAgAFAANgBw
+ AEwAkABiALAAeADPAI4A8ACkAP8RswD/Mb4A/1HHAP9x0QD/kdwA/7HlAP/R8AD///8AAAAAACwALwBL
+ AFAAaQBwAIcAkAClALAAxADPAOEA8ADwEf8A8jH/APRR/wD2cf8A95H/APmx/wD70f8A////AAAAAAAb
+ AC8ALQBQAD8AcABSAJAAYwCwAHYAzwCIAPAAmRH/AKYx/wC0Uf8AwnH/AM+R/wDcsf8A69H/AP///wAA
+ AAAACAAvAA4AUAAVAHAAGwCQACEAsAAmAM8ALADwAD4R/wBYMf8AcVH/AIxx/wCmkf8Av7H/ANrR/wD/
+ //8AAAAAAiYwJgIHSkpKSkkzBz1KSkEMAAAAJkpKSkAHPUpKSko7AAAAAAAAAAAAAAAAAAAAOUpKSj0C
+ SUpKSkoqAAIUFAIAAAACSUpKSkohHkpKSkodAAAAAAAAAAAAAAAAAgAUSkpKSkoXKUpKSkkMAAAAAAAA
+ AAAMSkpKSkorAB05ORsAAAAAAAAAAAAAAAAARBQZSkpKSkobAB4zLAwAAAAAAAAAAAAAQ0pKSkoZAAAA
+ BSQxHgIAAAAAAAAAAAAASkIFRUpKSkkFAAAAAAAAAAAAAAAAAAAAD0FKSSoAAAADQEpKSjMAAAAAAAAA
+ AAAASkoFFUJKQxcAAAAAAAAAAAAAAAAAAAAAAAIRBRMPAQAeSkpKSkoMAAAAAAAAAAAASkYCAAAHAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAHOUpKQg0mSkpKSkoOAAAAAAAAAAAASR4AAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAApSkpKSjgRSkpKSkMCAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAACKkE9GQA4SkpKSkUB
+ HERKPhMAAAAAAAAAAAAAOUlBFwAAAAAAAAAAAAAAAAAAAAAvSkpKSRcvSkpKSj0AAAEHAAAAAAAAAAAA
+ AAAASkpKSREAAAAAAAAAAAAAAAAAAAJFSkpKSjAKQ0pKRxUAAAAAAAAAAAAAAAAAAAAASkpKSiYAAAAA
+ AAAAAAAAAAAAAAdGSkpKSjAABx4gCQAAAAAAAAAAAAAAAAAAAAAASkpKSh4AAAAAAAAAAAAAAAAAAAAs
+ SUpKShUAAAAAAAAAAAAAAAAAAAAAAAAAAAAASkpKQwUAAAAAAAAAAAAAAAAAAAACJEE5FwAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAIzcsDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAXMzMXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlKSkpKGwAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADlKSkpKPQAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj1KSkpKQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAHyNKSkpKKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAALwIqRUUsAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAEXIQ8A
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAATdKSkokAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAF0pKSkpKDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAASjcFJkpKSkpKFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaIREAAAAAAAAA
+ AAAASko1D0pKSkpJBwAAAAAAAgAAAAAAAAAAAAAAAAAAAAAABj1KSkkeAAAAAAAAAAAASkpKAClKSkke
+ AgAAAAAAAAAAAAACAAAAAAAAAAACAgAAIUpKSkpFAgAAAAAAAAAASkpDAAAMFQURBQAAAAACAAAAAgAA
+ AAAAAAAAAjBKSTACL0pKSkpKCQAAAAAAAAAASkohAAAAEUFKSS8CAAAAAAAAAAAAAAAAAAAAKkpKSkoo
+ HEpKSkpDAAAAAAAAAAAALhcAAAAAPUpKSkoeAAAAAAIAAAAAAh4zLAwAQUpKSko+ATFKSkYVAAAAAAAA
+ AAAACS09LgkHSkpKSkozAAAAAAAAAAAAL0pKSkYJOkpKSko5AAANFAMAAAAAAAAAAAAAPkpKSkEHRkpK
+ SkopAAIAAAwXBQIHSUpKSkojGEpKSkkXAAAAAAAAAAAAAAAAAAAASkpKSkoZHkpKSkMFAAAAKUpKSR4M
+ SkpKSkoqABAtLw8AAAAAAAAAAAAAAAAAAAAASkpKSkoaABQpIQcAAAATSkpKSkkMPUpKSkoUAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAQ0pKSkYHAAAAGz5DKwceSkpKSkoXDDlKQx4AAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAEThGORMAAAAXSkpKSjAUSkpKSkoMAAICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAx
+ SkpKSkkCMEpKSSoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwSkpKSkUCABUhDgAC
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSkpKSisCAAAAAAAAAQAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFTg9JgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAgAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
+ AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
+ AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
+ AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
+ AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
+ AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
+ AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCKAAAACAAAABA
+ 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
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAwBQTFRFgICA////
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODgHVgAAAAlwSFlzAAAOvgAA
+ Dr4B6kKxwAAAABZJREFUGFdjYAABRhAAs4hlkq4DZDgACywAM12jTsYAAAAASUVORK5CYII=
+
+
+
+
+ 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
+ mXizAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAXJJREFUOE+lk0FL
+ AkEYhlvwv3jzoiDoQdCbdEnYf6CrqCgoHgRRAk/9EQVLdEGyFiQNMS+dvHnoEkgglGAmCL7NO6RMIZvU
+ wsMO3zzzzGk0ACf/+hjQNO1ccKlXKsYx0OUZeflXoFmtVsUS2P4CHboi0FQDrXK5jM12i/VmYwsduiLQ
+ UgNmqVTCuzj8tlrZQoeuCJhqoFMsFvG6XmO2WNhCh64IdNRAt1Ao4EXc/jSf20KHrgh01YCVy+Uwnkzw
+ vFzaQoeuCFhqoJfJZBCLxY6Crgj01EA/lUrB4/HA7XYfhHs78vk8A301MIzH4/B6vRiNHjAY3H+DM+7p
+ ug6fz4dsNsvAUA2Mo9Eo/H4/LOsOTqdTYprXEs64x0AwGEQ6nWZgrAYeDcNAIBBAu30r/6Reb0t2MwbC
+ 4TCSySQDj/uAeEyngqnL5fpoNG4QCoUktVpHspsxEIlEkEgk+AKnaoAP8kwwczgcF4fg3g+u9gEu/son
+ bfJW/NwRDyIAAAAASUVORK5CYII=
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAUNJREFUOE+lk79L
+ QlEcxW9/gqCrm6vg4uYoOAgOrqLk4ioP0r2Glhp0SSjoF1FE0BIUDU3RdIOGoKBVGlpapaHTObeuCPe6
+ 9ITD5fs9n3Pue8JbAWBS/VSQRvPwKR/j3JgaZXVqPv5TzPOXLhYoZDEcQidVWyhw3qzfn3tBAWH7PRjg
+ uV7HV5JAM6USyX50u86btlrOCwoOCR7Q+Oz1cFcu473dhmbppdFwu8dq1e3EBgU0zB6NXQJvzSaui0U8
+ VCq4LZWwn8vhLJ+HPDFiowUEzITADsGrQgFHmYzTSTYL7eSJiRZs0timRoTGhC956wXDXtrJEyM2eAIt
+ t34Be8NgTPLELCuQYe8Z9tK8ZBf+ieuEnxj20rzB26SYF7zCGsGEoVeW6NTMoJFiXlDAkFllqMOwTs2+
+ IOYFBf/9oFJ9ibr0B4f94vVG3bWDAAAAAElFTkSuQmCC
+
+
\ No newline at end of file
diff --git a/GreenshotPlugin/Core/HResultExtensions.cs b/GreenshotPlugin/Core/HResultExtensions.cs
new file mode 100644
index 000000000..bc0f8e4ba
--- /dev/null
+++ b/GreenshotPlugin/Core/HResultExtensions.cs
@@ -0,0 +1,65 @@
+// Greenshot - a free and open source screenshot tool
+// Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+//
+// For more information see: http://getgreenshot.org/
+// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 1 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using GreenshotPlugin.Core.Enums;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+
+namespace GreenshotPlugin.Core
+{
+ ///
+ /// Extensions to handle the HResult
+ ///
+ public static class HResultExtensions
+ {
+ ///
+ /// Test if the HResult respresents a fail
+ ///
+ /// HResult
+ /// bool
+ [Pure]
+ public static bool Failed(this HResult hResult)
+ {
+ return hResult < 0;
+ }
+
+ ///
+ /// Test if the HResult respresents a success
+ ///
+ /// HResult
+ /// bool
+ [Pure]
+ public static bool Succeeded(this HResult hResult)
+ {
+ return hResult >= HResult.S_OK;
+ }
+
+ ///
+ /// Throw an exception on Failure
+ ///
+ /// HResult
+ public static void ThrowOnFailure(this HResult hResult)
+ {
+ if (hResult.Failed())
+ {
+ throw Marshal.GetExceptionForHR((int)hResult);
+ }
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/IEHelper.cs b/GreenshotPlugin/Core/IEHelper.cs
new file mode 100644
index 000000000..703d42a15
--- /dev/null
+++ b/GreenshotPlugin/Core/IEHelper.cs
@@ -0,0 +1,189 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using log4net;
+using Microsoft.Win32;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Description of IEHelper.
+ ///
+ public static class IEHelper {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(IEHelper));
+ // Internet explorer Registry key
+ private const string IeKey = @"Software\Microsoft\Internet Explorer";
+
+ ///
+ /// Get the current browser version
+ ///
+ /// int with browser version
+ public static int IEVersion
+ {
+ get
+ {
+ var maxVer = 7;
+ using (var ieKey = Registry.LocalMachine.OpenSubKey(IeKey, false))
+ {
+ foreach (var value in new[] { "svcVersion", "svcUpdateVersion", "Version", "W2kVersion" })
+ {
+ var objVal = ieKey.GetValue(value, "0");
+ var strVal = Convert.ToString(objVal);
+
+ var iPos = strVal.IndexOf('.');
+ if (iPos > 0)
+ {
+ strVal = strVal.Substring(0, iPos);
+ }
+
+ if (int.TryParse(strVal, out var res))
+ {
+ maxVer = Math.Max(maxVer, res);
+ }
+ }
+ }
+
+ return maxVer;
+ }
+ }
+
+ ///
+ /// Get the highest possible version for the embedded browser
+ ///
+ /// true to ignore the doctype when loading a page
+ /// IE Feature
+ public static int GetEmbVersion(bool ignoreDoctype = true)
+ {
+ var ieVersion = IEVersion;
+
+ if (ieVersion > 9)
+ {
+ return ieVersion * 1000 + (ignoreDoctype ? 1 : 0);
+ }
+
+ if (ieVersion > 7)
+ {
+ return ieVersion * 1111;
+ }
+
+ return 7000;
+ }
+
+ ///
+ /// Fix browser version to the highest possible
+ ///
+ /// true to ignore the doctype when loading a page
+ public static void FixBrowserVersion(bool ignoreDoctype = true)
+ {
+ var applicationName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
+ FixBrowserVersion(applicationName, ignoreDoctype);
+ }
+
+ ///
+ /// Fix the browser version for the specified application
+ ///
+ /// Name of the process
+ /// true to ignore the doctype when loading a page
+ public static void FixBrowserVersion(string applicationName, bool ignoreDoctype = true)
+ {
+ FixBrowserVersion(applicationName, GetEmbVersion(ignoreDoctype));
+ }
+
+ ///
+ /// Fix the browser version for the specified application
+ ///
+ /// Name of the process
+ ///
+ /// Version, see
+ /// Browser Emulation
+ ///
+ public static void FixBrowserVersion(string applicationName, int ieVersion)
+ {
+ ModifyRegistry("HKEY_CURRENT_USER", applicationName + ".exe", ieVersion);
+#if DEBUG
+ ModifyRegistry("HKEY_CURRENT_USER", applicationName + ".vshost.exe", ieVersion);
+#endif
+ }
+
+ ///
+ /// Make the change to the registry
+ ///
+ /// HKEY_CURRENT_USER or something
+ /// Name of the executable
+ /// Version to use
+ private static void ModifyRegistry(string root, string applicationName, int ieFeatureVersion)
+ {
+ var regKey = root + @"\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
+ try
+ {
+ Registry.SetValue(regKey, applicationName, ieFeatureVersion);
+ }
+ catch (Exception ex)
+ {
+ // some config will hit access rights exceptions
+ // this is why we try with both LOCAL_MACHINE and CURRENT_USER
+ Log.Error(ex);
+ Log.ErrorFormat("couldn't modify the registry key {0}", regKey);
+ }
+ }
+
+ ///
+ /// Find the DirectUI window for MSAA (Accessible)
+ ///
+ /// The browser WindowDetails
+ /// WindowDetails for the DirectUI window
+ public static WindowDetails GetDirectUI(WindowDetails browserWindowDetails) {
+ if (browserWindowDetails == null) {
+ return null;
+ }
+ WindowDetails tmpWd = browserWindowDetails;
+ // Since IE 9 the TabBandClass is less deep!
+ if (IEVersion < 9) {
+ tmpWd = tmpWd.GetChild("CommandBarClass");
+ tmpWd = tmpWd?.GetChild("ReBarWindow32");
+ }
+ tmpWd = tmpWd?.GetChild("TabBandClass");
+ tmpWd = tmpWd?.GetChild("DirectUIHWND");
+ return tmpWd;
+ }
+
+ ///
+ /// Return an IEnumerable with the currently opened IE urls
+ ///
+ ///
+ public static IEnumerable GetIEUrls() {
+ // Find the IE window
+ foreach (WindowDetails ieWindow in WindowDetails.GetAllWindows("IEFrame")) {
+ WindowDetails directUIWD = GetDirectUI(ieWindow);
+ if (directUIWD != null) {
+ Accessible ieAccessible = new Accessible(directUIWD.Handle);
+ foreach(string url in ieAccessible.IETabUrls)
+ {
+ yield return url;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/IImage.cs b/GreenshotPlugin/Core/IImage.cs
new file mode 100644
index 000000000..53fa64bd3
--- /dev/null
+++ b/GreenshotPlugin/Core/IImage.cs
@@ -0,0 +1,68 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace GreenshotPlugin.Core
+{
+ ///
+ /// The image interface, this abstracts an image
+ ///
+ public interface IImage : IDisposable
+ {
+ ///
+ /// Height of the image, can be set to change
+ ///
+ int Height { get; set; }
+
+ ///
+ /// Width of the image, can be set to change.
+ ///
+ int Width { get; set; }
+
+ ///
+ /// Size of the image
+ ///
+ Size Size { get; }
+
+ ///
+ /// Pixelformat of the underlying image
+ ///
+ PixelFormat PixelFormat { get; }
+
+ ///
+ /// Vertical resolution of the underlying image
+ ///
+ float VerticalResolution { get; }
+
+ ///
+ /// Horizontal resolution of the underlying image
+ ///
+ float HorizontalResolution { get; }
+
+ ///
+ /// Unterlying image, or an on demand rendered version with different attributes as the original
+ ///
+ Image Image { get; }
+ }
+}
diff --git a/GreenshotPlugin/Core/ImageHelper.cs b/GreenshotPlugin/Core/ImageHelper.cs
new file mode 100644
index 000000000..8c31eaf4c
--- /dev/null
+++ b/GreenshotPlugin/Core/ImageHelper.cs
@@ -0,0 +1,1724 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using GreenshotPlugin.UnmanagedHelpers;
+using GreenshotPlugin.Effects;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using log4net;
+
+namespace GreenshotPlugin.Core {
+ internal enum ExifOrientations : byte {
+ Unknown = 0,
+ TopLeft = 1,
+ TopRight = 2,
+ BottomRight = 3,
+ BottomLeft = 4,
+ LeftTop = 5,
+ RightTop = 6,
+ RightBottom = 7,
+ LeftBottom = 8,
+ }
+
+ ///
+ /// Description of ImageHelper.
+ ///
+ public static class ImageHelper {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ImageHelper));
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ private const int ExifOrientationId = 0x0112;
+
+ static ImageHelper()
+ {
+ StreamConverters["greenshot"] = (stream, s) =>
+ {
+ var surface = SimpleServiceProvider.Current.GetInstance>().Invoke();
+ return surface.GetImageForExport();
+ };
+
+ // Add a SVG converter
+ StreamConverters["svg"] = (stream, s) =>
+ {
+ stream.Position = 0;
+ try
+ {
+ return SvgImage.FromStream(stream).Image;
+ }
+ catch (Exception ex)
+ {
+ Log.Error("Can't load SVG", ex);
+ }
+ return null;
+ };
+
+ static Image DefaultConverter(Stream stream, string s)
+ {
+ stream.Position = 0;
+ using var tmpImage = Image.FromStream(stream, true, true);
+ Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
+ return Clone(tmpImage, PixelFormat.Format32bppArgb);
+ }
+
+ // Fallback
+ StreamConverters[string.Empty] = DefaultConverter;
+ StreamConverters["gif"] = DefaultConverter;
+ StreamConverters["bmp"] = DefaultConverter;
+ StreamConverters["jpg"] = DefaultConverter;
+ StreamConverters["jpeg"] = DefaultConverter;
+ StreamConverters["png"] = DefaultConverter;
+ StreamConverters["wmf"] = DefaultConverter;
+
+ StreamConverters["ico"] = (stream, extension) =>
+ {
+ // Icon logic, try to get the Vista icon, else the biggest possible
+ try
+ {
+ using Image tmpImage = ExtractVistaIcon(stream);
+ if (tmpImage != null)
+ {
+ return Clone(tmpImage, PixelFormat.Format32bppArgb);
+ }
+ }
+ catch (Exception vistaIconException)
+ {
+ Log.Warn("Can't read icon", vistaIconException);
+ }
+ try
+ {
+ // No vista icon, try normal icon
+ stream.Position = 0;
+ // We create a copy of the bitmap, so everything else can be disposed
+ using Icon tmpIcon = new Icon(stream, new Size(1024, 1024));
+ using Image tmpImage = tmpIcon.ToBitmap();
+ return Clone(tmpImage, PixelFormat.Format32bppArgb);
+ }
+ catch (Exception iconException)
+ {
+ Log.Warn("Can't read icon", iconException);
+ }
+
+ stream.Position = 0;
+ return DefaultConverter(stream, extension);
+ };
+ }
+
+ public static IDictionary> StreamConverters { get; } = new Dictionary>();
+
+ ///
+ /// Make sure the image is orientated correctly
+ ///
+ ///
+ public static void Orientate(Image image)
+ {
+ if (!CoreConfig.ProcessEXIFOrientation)
+ {
+ return;
+ }
+ try
+ {
+ // Get the index of the orientation property.
+ int orientationIndex = Array.IndexOf(image.PropertyIdList, ExifOrientationId);
+ // If there is no such property, return Unknown.
+ if (orientationIndex < 0)
+ {
+ return;
+ }
+ PropertyItem item = image.GetPropertyItem(ExifOrientationId);
+
+ ExifOrientations orientation = (ExifOrientations)item.Value[0];
+ // Orient the image.
+ switch (orientation)
+ {
+ case ExifOrientations.Unknown:
+ case ExifOrientations.TopLeft:
+ break;
+ case ExifOrientations.TopRight:
+ image.RotateFlip(RotateFlipType.RotateNoneFlipX);
+ break;
+ case ExifOrientations.BottomRight:
+ image.RotateFlip(RotateFlipType.Rotate180FlipNone);
+ break;
+ case ExifOrientations.BottomLeft:
+ image.RotateFlip(RotateFlipType.RotateNoneFlipY);
+ break;
+ case ExifOrientations.LeftTop:
+ image.RotateFlip(RotateFlipType.Rotate90FlipX);
+ break;
+ case ExifOrientations.RightTop:
+ image.RotateFlip(RotateFlipType.Rotate90FlipNone);
+ break;
+ case ExifOrientations.RightBottom:
+ image.RotateFlip(RotateFlipType.Rotate90FlipY);
+ break;
+ case ExifOrientations.LeftBottom:
+ image.RotateFlip(RotateFlipType.Rotate270FlipNone);
+ break;
+ }
+ // Set the orientation to be normal, as we rotated the image.
+ item.Value[0] = (byte)ExifOrientations.TopLeft;
+ image.SetPropertyItem(item);
+ }
+ catch (Exception orientEx)
+ {
+ Log.Warn("Problem orientating the image: ", orientEx);
+ }
+ }
+
+ ///
+ /// Create a Thumbnail
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight, int maxWidth = -1, int maxHeight = -1)
+ {
+ int srcWidth = image.Width;
+ int srcHeight = image.Height;
+ if (thumbHeight < 0)
+ {
+ thumbHeight = (int)(thumbWidth * (srcHeight / (float)srcWidth));
+ }
+ if (thumbWidth < 0)
+ {
+ thumbWidth = (int)(thumbHeight * (srcWidth / (float)srcHeight));
+ }
+ if (maxWidth > 0 && thumbWidth > maxWidth)
+ {
+ thumbWidth = Math.Min(thumbWidth, maxWidth);
+ thumbHeight = (int)(thumbWidth * (srcHeight / (float)srcWidth));
+ }
+ if (maxHeight > 0 && thumbHeight > maxHeight)
+ {
+ thumbHeight = Math.Min(thumbHeight, maxHeight);
+ thumbWidth = (int)(thumbHeight * (srcWidth / (float)srcHeight));
+ }
+
+ Bitmap bmp = new Bitmap(thumbWidth, thumbHeight);
+ using (Graphics graphics = Graphics.FromImage(bmp))
+ {
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ Rectangle rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight);
+ graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel);
+ }
+ return bmp;
+ }
+
+ ///
+ /// Crops the image to the specified rectangle
+ ///
+ /// Image to crop
+ /// Rectangle with bitmap coordinates, will be "intersected" to the bitmap
+ public static bool Crop(ref Image image, ref Rectangle cropRectangle)
+ {
+ if (image is Bitmap && (image.Width * image.Height > 0))
+ {
+ cropRectangle.Intersect(new Rectangle(0, 0, image.Width, image.Height));
+ if (cropRectangle.Width != 0 || cropRectangle.Height != 0)
+ {
+ Image returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare);
+ image.Dispose();
+ image = returnImage;
+ return true;
+ }
+ }
+ Log.Warn("Can't crop a null/zero size image!");
+ return false;
+ }
+
+ ///
+ /// Private helper method for the FindAutoCropRectangle
+ ///
+ ///
+ ///
+ ///
+ /// Rectangle
+ private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference)
+ {
+ Rectangle cropRectangle = Rectangle.Empty;
+ Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y);
+ Point min = new Point(int.MaxValue, int.MaxValue);
+ Point max = new Point(int.MinValue, int.MinValue);
+
+ if (cropDifference > 0)
+ {
+ for (int y = 0; y < fastBitmap.Height; y++)
+ {
+ for (int x = 0; x < fastBitmap.Width; x++)
+ {
+ Color currentColor = fastBitmap.GetColorAt(x, y);
+ int diffR = Math.Abs(currentColor.R - referenceColor.R);
+ int diffG = Math.Abs(currentColor.G - referenceColor.G);
+ int diffB = Math.Abs(currentColor.B - referenceColor.B);
+ if ((diffR + diffG + diffB) / 3 <= cropDifference)
+ {
+ continue;
+ }
+ if (x < min.X) min.X = x;
+ if (y < min.Y) min.Y = y;
+ if (x > max.X) max.X = x;
+ if (y > max.Y) max.Y = y;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < fastBitmap.Height; y++)
+ {
+ for (int x = 0; x < fastBitmap.Width; x++)
+ {
+ Color currentColor = fastBitmap.GetColorAt(x, y);
+ if (!referenceColor.Equals(currentColor))
+ {
+ continue;
+ }
+ if (x < min.X) min.X = x;
+ if (y < min.Y) min.Y = y;
+ if (x > max.X) max.X = x;
+ if (y > max.Y) max.Y = y;
+ }
+ }
+ }
+
+ if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1))))
+ {
+ if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue))
+ {
+ cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1);
+ }
+ }
+ return cropRectangle;
+ }
+
+ ///
+ /// Get a rectangle for the image which crops the image of all colors equal to that on 0,0
+ ///
+ ///
+ ///
+ /// Rectangle
+ public static Rectangle FindAutoCropRectangle(Image image, int cropDifference)
+ {
+ Rectangle cropRectangle = Rectangle.Empty;
+ var checkPoints = new List
+ {
+ new Point(0, 0),
+ new Point(0, image.Height - 1),
+ new Point(image.Width - 1, 0),
+ new Point(image.Width - 1, image.Height - 1)
+ };
+ // Top Left
+ // Bottom Left
+ // Top Right
+ // Bottom Right
+ using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap)image))
+ {
+ // find biggest area
+ foreach (Point checkPoint in checkPoints)
+ {
+ var currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference);
+ if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height)
+ {
+ cropRectangle = currentRectangle;
+ }
+ }
+ }
+ return cropRectangle;
+ }
+
+ ///
+ /// Load an image from file
+ ///
+ ///
+ ///
+ public static Image LoadImage(string filename)
+ {
+ if (string.IsNullOrEmpty(filename))
+ {
+ return null;
+ }
+ if (!File.Exists(filename))
+ {
+ return null;
+ }
+ Image fileImage;
+ Log.InfoFormat("Loading image from file {0}", filename);
+ // Fixed lock problem Bug #3431881
+ using (Stream imageFileStream = File.OpenRead(filename))
+ {
+ fileImage = FromStream(imageFileStream, Path.GetExtension(filename));
+ }
+ if (fileImage != null)
+ {
+ Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat, fileImage.HorizontalResolution, fileImage.VerticalResolution);
+ }
+ return fileImage;
+ }
+
+ ///
+ /// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx
+ /// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx
+ ///
+ /// Stream with the icon information
+ /// Bitmap with the Vista Icon (256x256)
+ private static Bitmap ExtractVistaIcon(Stream iconStream)
+ {
+ const int sizeIconDir = 6;
+ const int sizeIconDirEntry = 16;
+ Bitmap bmpPngExtracted = null;
+ try
+ {
+ byte[] srcBuf = new byte[iconStream.Length];
+ iconStream.Read(srcBuf, 0, (int)iconStream.Length);
+ int iCount = BitConverter.ToInt16(srcBuf, 4);
+ for (int iIndex = 0; iIndex < iCount; iIndex++)
+ {
+ int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex];
+ int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1];
+ if (iWidth == 0 && iHeight == 0)
+ {
+ int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8);
+ int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12);
+ using MemoryStream destStream = new MemoryStream();
+ destStream.Write(srcBuf, iImageOffset, iImageSize);
+ destStream.Seek(0, SeekOrigin.Begin);
+ bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
+ break;
+ }
+ }
+ }
+ catch
+ {
+ return null;
+ }
+ return bmpPngExtracted;
+ }
+
+ ///
+ /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
+ ///
+ /// The file (EXE or DLL) to get the icon from
+ /// Index of the icon
+ /// true if the large icon is wanted
+ /// Icon
+ public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge)
+ {
+ Shell32.ExtractIconEx(location, index, out var large, out var small, 1);
+ Icon returnIcon = null;
+ bool isLarge = false;
+ bool isSmall = false;
+ try
+ {
+ if (takeLarge && !IntPtr.Zero.Equals(large))
+ {
+ returnIcon = Icon.FromHandle(large);
+ isLarge = true;
+ }
+ else if (!IntPtr.Zero.Equals(small))
+ {
+ returnIcon = Icon.FromHandle(small);
+ isSmall = true;
+ }
+ else if (!IntPtr.Zero.Equals(large))
+ {
+ returnIcon = Icon.FromHandle(large);
+ isLarge = true;
+ }
+ }
+ finally
+ {
+ if (isLarge && !IntPtr.Zero.Equals(small))
+ {
+ User32.DestroyIcon(small);
+ }
+ if (isSmall && !IntPtr.Zero.Equals(large))
+ {
+ User32.DestroyIcon(large);
+ }
+ }
+ return returnIcon;
+ }
+
+ ///
+ /// Get the number of icon in the file
+ ///
+ /// Location of the EXE or DLL
+ ///
+ public static int CountAssociatedIcons(string location)
+ {
+ return Shell32.ExtractIconEx(location, -1, out _, out _, 0);
+ }
+
+ ///
+ /// Apply the effect to the bitmap
+ ///
+ /// Bitmap
+ /// IEffect
+ ///
+ /// Bitmap
+ public static Image ApplyEffect(Image sourceImage, IEffect effect, Matrix matrix)
+ {
+ var effects = new List { effect };
+ return ApplyEffects(sourceImage, effects, matrix);
+ }
+
+ ///
+ /// Apply the effects in the supplied order to the bitmap
+ ///
+ /// Bitmap
+ /// List of IEffect
+ ///
+ /// Bitmap
+ public static Image ApplyEffects(Image sourceImage, IEnumerable effects, Matrix matrix)
+ {
+ var currentImage = sourceImage;
+ bool disposeImage = false;
+ foreach (var effect in effects)
+ {
+ var tmpImage = effect.Apply(currentImage, matrix);
+ if (tmpImage != null)
+ {
+ if (disposeImage)
+ {
+ currentImage.Dispose();
+ }
+ currentImage = tmpImage;
+ // Make sure the "new" image is disposed
+ disposeImage = true;
+ }
+ }
+ return currentImage;
+ }
+
+ ///
+ /// Helper method for the tornedge
+ ///
+ /// Path to draw to
+ /// Points for the lines to draw
+ private static void DrawLines(GraphicsPath path, List points)
+ {
+ path.AddLine(points[0], points[1]);
+ for (int i = 0; i < points.Count - 1; i++)
+ {
+ path.AddLine(points[i], points[i + 1]);
+ }
+ }
+
+ ///
+ /// Make the picture look like it's torn
+ ///
+ /// Bitmap to make torn edge off
+ /// How large (height) is each tooth
+ /// How wide is a horizontal tooth
+ /// How wide is a vertical tooth
+ /// bool[] with information on if the edge needs torn or not. Order is clockwise: 0=top,1=right,2=bottom,3=left
+ /// Changed bitmap
+ public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges)
+ {
+ Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ using (var path = new GraphicsPath())
+ {
+ Random random = new Random();
+ int horizontalRegions = (int)Math.Round((float)sourceImage.Width / horizontalToothRange);
+ int verticalRegions = (int)Math.Round((float)sourceImage.Height / verticalToothRange);
+
+ Point topLeft = new Point(0, 0);
+ Point topRight = new Point(sourceImage.Width, 0);
+ Point bottomLeft = new Point(0, sourceImage.Height);
+ Point bottomRight = new Point(sourceImage.Width, sourceImage.Height);
+
+ List points = new List();
+
+ if (edges[0])
+ {
+ // calculate starting point only if the left edge is torn
+ if (!edges[3])
+ {
+ points.Add(topLeft);
+ }
+ else
+ {
+ points.Add(new Point(random.Next(1, toothHeight), random.Next(1, toothHeight)));
+ }
+ for (int i = 1; i < horizontalRegions - 1; i++)
+ {
+ points.Add(new Point(i * horizontalToothRange, random.Next(1, toothHeight)));
+ }
+ points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), random.Next(1, toothHeight)));
+ }
+ else
+ {
+ // set start & endpoint to be the default "whole-line"
+ points.Add(topLeft);
+ points.Add(topRight);
+ }
+ // Right
+ if (edges[1])
+ {
+ for (int i = 1; i < verticalRegions - 1; i++)
+ {
+ points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), i * verticalToothRange));
+ }
+ points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight)));
+ }
+ else
+ {
+ // correct previous ending point
+ points[points.Count - 1] = topRight;
+ // set endpoint to be the default "whole-line"
+ points.Add(bottomRight);
+ }
+ // Bottom
+ if (edges[2])
+ {
+ for (int i = 1; i < horizontalRegions - 1; i++)
+ {
+ points.Add(new Point(sourceImage.Width - i * horizontalToothRange, sourceImage.Height - random.Next(1, toothHeight)));
+ }
+ points.Add(new Point(random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight)));
+ }
+ else
+ {
+ // correct previous ending point
+ points[points.Count - 1] = bottomRight;
+ // set endpoint to be the default "whole-line"
+ points.Add(bottomLeft);
+ }
+ // Left
+ if (edges[3])
+ {
+ // One fewer as the end point is the starting point
+ for (int i = 1; i < verticalRegions - 1; i++)
+ {
+ points.Add(new Point(random.Next(1, toothHeight), points[points.Count - 1].Y - verticalToothRange));
+ }
+ }
+ else
+ {
+ // correct previous ending point
+ points[points.Count - 1] = bottomLeft;
+ // set endpoint to be the default "whole-line"
+ points.Add(topLeft);
+ }
+ // End point always is the starting point
+ points[points.Count - 1] = points[0];
+
+ DrawLines(path, points);
+
+ path.CloseFigure();
+
+ // Draw the created figure with the original image by using a TextureBrush so we have anti-aliasing
+ using Graphics graphics = Graphics.FromImage(returnImage);
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ using Brush brush = new TextureBrush(sourceImage);
+ // Important note: If the target wouldn't be at 0,0 we need to translate-transform!!
+ graphics.FillPath(brush, path);
+ }
+ return returnImage;
+ }
+
+ ///
+ /// Apply BoxBlur to the destinationBitmap
+ ///
+ /// Bitmap to blur
+ /// Must be ODD!
+ public static void ApplyBoxBlur(Bitmap destinationBitmap, int range)
+ {
+ // We only need one fastbitmap as we use it as source and target (the reading is done for one line H/V, writing after "parsing" one line H/V)
+ using IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap);
+ ApplyBoxBlur(fastBitmap, range);
+ }
+
+ ///
+ /// Apply BoxBlur to the fastBitmap
+ ///
+ /// IFastBitmap to blur
+ /// Must be ODD!
+ public static void ApplyBoxBlur(IFastBitmap fastBitmap, int range)
+ {
+ // Range must be odd!
+ if ((range & 1) == 0)
+ {
+ range++;
+ }
+ if (range <= 1)
+ {
+ return;
+ }
+ // Box blurs are frequently used to approximate a Gaussian blur.
+ // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel.
+ // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur.
+ // (Might also be a mistake in our blur, but for now it looks great)
+ if (fastBitmap.HasAlphaChannel)
+ {
+ BoxBlurHorizontalAlpha(fastBitmap, range);
+ BoxBlurVerticalAlpha(fastBitmap, range);
+ BoxBlurHorizontalAlpha(fastBitmap, range);
+ BoxBlurVerticalAlpha(fastBitmap, range);
+ }
+ else
+ {
+ BoxBlurHorizontal(fastBitmap, range);
+ BoxBlurVertical(fastBitmap, range);
+ BoxBlurHorizontal(fastBitmap, range);
+ BoxBlurVertical(fastBitmap, range);
+ }
+ }
+
+ ///
+ /// BoxBlurHorizontal is a private helper method for the BoxBlur
+ ///
+ /// Target BitmapBuffer
+ /// Range must be odd!
+ private static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range)
+ {
+ if (targetFastBitmap.HasAlphaChannel)
+ {
+ throw new NotSupportedException("BoxBlurHorizontal should NOT be called for bitmaps with alpha channel");
+ }
+ int halfRange = range / 2;
+ Color[] newColors = new Color[targetFastBitmap.Width];
+ byte[] tmpColor = new byte[3];
+ for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
+ {
+ int hits = 0;
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++)
+ {
+ int oldPixel = x - halfRange - 1;
+ if (oldPixel >= targetFastBitmap.Left)
+ {
+ targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
+ r -= tmpColor[FastBitmap.ColorIndexR];
+ g -= tmpColor[FastBitmap.ColorIndexG];
+ b -= tmpColor[FastBitmap.ColorIndexB];
+ hits--;
+ }
+
+ int newPixel = x + halfRange;
+ if (newPixel < targetFastBitmap.Right)
+ {
+ targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
+ r += tmpColor[FastBitmap.ColorIndexR];
+ g += tmpColor[FastBitmap.ColorIndexG];
+ b += tmpColor[FastBitmap.ColorIndexB];
+ hits++;
+ }
+
+ if (x >= targetFastBitmap.Left)
+ {
+ newColors[x - targetFastBitmap.Left] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
+ }
+ }
+ for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
+ {
+ targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]);
+ }
+ }
+ }
+ ///
+ /// BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel
+ ///
+ /// Target BitmapBuffer
+ /// Range must be odd!
+ private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range)
+ {
+ if (!targetFastBitmap.HasAlphaChannel)
+ {
+ throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel");
+ }
+ int halfRange = range / 2;
+ Color[] newColors = new Color[targetFastBitmap.Width];
+ byte[] tmpColor = new byte[4];
+ for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
+ {
+ int hits = 0;
+ int a = 0;
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++)
+ {
+ int oldPixel = x - halfRange - 1;
+ if (oldPixel >= targetFastBitmap.Left)
+ {
+ targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
+ a -= tmpColor[FastBitmap.ColorIndexA];
+ r -= tmpColor[FastBitmap.ColorIndexR];
+ g -= tmpColor[FastBitmap.ColorIndexG];
+ b -= tmpColor[FastBitmap.ColorIndexB];
+ hits--;
+ }
+
+ int newPixel = x + halfRange;
+ if (newPixel < targetFastBitmap.Right)
+ {
+ targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
+ a += tmpColor[FastBitmap.ColorIndexA];
+ r += tmpColor[FastBitmap.ColorIndexR];
+ g += tmpColor[FastBitmap.ColorIndexG];
+ b += tmpColor[FastBitmap.ColorIndexB];
+ hits++;
+ }
+
+ if (x >= targetFastBitmap.Left)
+ {
+ newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
+ }
+ }
+ for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
+ {
+ targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]);
+ }
+ }
+ }
+
+ ///
+ /// BoxBlurVertical is a private helper method for the BoxBlur
+ ///
+ /// BitmapBuffer which previously was created with BoxBlurHorizontal
+ /// Range must be odd!
+ private static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range)
+ {
+ if (targetFastBitmap.HasAlphaChannel)
+ {
+ throw new NotSupportedException("BoxBlurVertical should NOT be called for bitmaps with alpha channel");
+ }
+ int halfRange = range / 2;
+ Color[] newColors = new Color[targetFastBitmap.Height];
+ byte[] tmpColor = new byte[4];
+ for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
+ {
+ int hits = 0;
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++)
+ {
+ int oldPixel = y - halfRange - 1;
+ if (oldPixel >= targetFastBitmap.Top)
+ {
+ targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
+ r -= tmpColor[FastBitmap.ColorIndexR];
+ g -= tmpColor[FastBitmap.ColorIndexG];
+ b -= tmpColor[FastBitmap.ColorIndexB];
+ hits--;
+ }
+
+ int newPixel = y + halfRange;
+ if (newPixel < targetFastBitmap.Bottom)
+ {
+ targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
+ r += tmpColor[FastBitmap.ColorIndexR];
+ g += tmpColor[FastBitmap.ColorIndexG];
+ b += tmpColor[FastBitmap.ColorIndexB];
+ hits++;
+ }
+
+ if (y >= targetFastBitmap.Top)
+ {
+ newColors[y - targetFastBitmap.Top] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
+ }
+ }
+
+ for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
+ {
+ targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]);
+ }
+ }
+ }
+
+ ///
+ /// BoxBlurVertical is a private helper method for the BoxBlur
+ ///
+ /// BitmapBuffer which previously was created with BoxBlurHorizontal
+ /// Range must be odd!
+ private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range)
+ {
+ if (!targetFastBitmap.HasAlphaChannel)
+ {
+ throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel");
+ }
+
+ int halfRange = range / 2;
+ Color[] newColors = new Color[targetFastBitmap.Height];
+ byte[] tmpColor = new byte[4];
+ for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
+ {
+ int hits = 0;
+ int a = 0;
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++)
+ {
+ int oldPixel = y - halfRange - 1;
+ if (oldPixel >= targetFastBitmap.Top)
+ {
+ targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
+ a -= tmpColor[FastBitmap.ColorIndexA];
+ r -= tmpColor[FastBitmap.ColorIndexR];
+ g -= tmpColor[FastBitmap.ColorIndexG];
+ b -= tmpColor[FastBitmap.ColorIndexB];
+ hits--;
+ }
+
+ int newPixel = y + halfRange;
+ if (newPixel < targetFastBitmap.Bottom)
+ {
+ //int colorg = pixels[index + newPixelOffset];
+ targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
+ a += tmpColor[FastBitmap.ColorIndexA];
+ r += tmpColor[FastBitmap.ColorIndexR];
+ g += tmpColor[FastBitmap.ColorIndexG];
+ b += tmpColor[FastBitmap.ColorIndexB];
+ hits++;
+ }
+
+ if (y >= targetFastBitmap.Top)
+ {
+ newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
+ }
+ }
+
+ for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
+ {
+ targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]);
+ }
+ }
+ }
+
+ ///
+ /// This method fixes the problem that we can't apply a filter outside the target bitmap,
+ /// therefor the filtered-bitmap will be shifted if we try to draw it outside the target bitmap.
+ /// It will also account for the Invert flag.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert)
+ {
+ Rectangle myRect;
+ if (invert)
+ {
+ myRect = new Rectangle(0, 0, applySize.Width, applySize.Height);
+ }
+ else
+ {
+ Rectangle applyRect = new Rectangle(0, 0, applySize.Width, applySize.Height);
+ myRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
+ myRect.Intersect(applyRect);
+ }
+ return myRect;
+ }
+
+ ///
+ /// Create a new bitmap where the sourceBitmap has a shadow
+ ///
+ /// Bitmap to make a shadow on
+ /// How dark is the shadow
+ /// Size of the shadow
+ /// What pixel format must the returning bitmap have
+ ///
+ /// The transform matrix which describes how the elements need to be transformed to stay at the same location
+ /// Bitmap with the shadow, is bigger than the sourceBitmap!!
+ public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, Matrix matrix, PixelFormat targetPixelformat)
+ {
+ Point offset = shadowOffset;
+ offset.X += shadowSize - 1;
+ offset.Y += shadowSize - 1;
+ matrix.Translate(offset.X, offset.Y, MatrixOrder.Append);
+ // Create a new "clean" image
+ Bitmap returnImage = CreateEmpty(sourceBitmap.Width + shadowSize * 2, sourceBitmap.Height + shadowSize * 2, targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
+ // Make sure the shadow is odd, there is no reason for an even blur!
+ if ((shadowSize & 1) == 0)
+ {
+ shadowSize++;
+ }
+ bool useGdiBlur = GDIplus.IsBlurPossible(shadowSize);
+ // Create "mask" for the shadow
+ ColorMatrix maskMatrix = new ColorMatrix
+ {
+ Matrix00 = 0,
+ Matrix11 = 0,
+ Matrix22 = 0
+ };
+ if (useGdiBlur)
+ {
+ maskMatrix.Matrix33 = darkness + 0.1f;
+ }
+ else
+ {
+ maskMatrix.Matrix33 = darkness;
+ }
+ Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size);
+ ApplyColorMatrix((Bitmap)sourceBitmap, Rectangle.Empty, returnImage, shadowRectangle, maskMatrix);
+
+ // blur "shadow", apply to whole new image
+ if (useGdiBlur)
+ {
+ // Use GDI Blur
+ Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height);
+ GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize + 1, false);
+ }
+ else
+ {
+ // try normal software blur
+ //returnImage = CreateBlur(returnImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle);
+ ApplyBoxBlur(returnImage, shadowSize);
+ }
+
+ // Draw the original image over the shadow
+ using (Graphics graphics = Graphics.FromImage(returnImage))
+ {
+ // Make sure we draw with the best quality!
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ // draw original with a TextureBrush so we have nice antialiasing!
+ using Brush textureBrush = new TextureBrush(sourceBitmap, WrapMode.Clamp);
+ // We need to do a translate-transform otherwise the image is wrapped
+ graphics.TranslateTransform(offset.X, offset.Y);
+ graphics.FillRectangle(textureBrush, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
+ }
+ return returnImage;
+ }
+
+ ///
+ /// Return negative of Bitmap
+ ///
+ /// Bitmap to create a negative off
+ /// Negative bitmap
+ public static Bitmap CreateNegative(Image sourceImage)
+ {
+ Bitmap clone = (Bitmap)Clone(sourceImage);
+ ColorMatrix invertMatrix = new ColorMatrix(new[] {
+ new float[] {-1, 0, 0, 0, 0},
+ new float[] {0, -1, 0, 0, 0},
+ new float[] {0, 0, -1, 0, 0},
+ new float[] {0, 0, 0, 1, 0},
+ new float[] {1, 1, 1, 1, 1}
+ });
+ ApplyColorMatrix(clone, invertMatrix);
+ return clone;
+ }
+ ///
+ /// Apply a color matrix to the image
+ ///
+ /// Image to apply matrix to
+ /// ColorMatrix to apply
+ public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix)
+ {
+ ApplyColorMatrix(source, Rectangle.Empty, source, Rectangle.Empty, colorMatrix);
+ }
+
+ ///
+ /// Apply a color matrix by copying from the source to the destination
+ ///
+ /// Image to copy from
+ /// Rectangle to copy from
+ /// Rectangle to copy to
+ /// Image to copy to
+ /// ColorMatrix to apply
+ public static void ApplyColorMatrix(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ColorMatrix colorMatrix)
+ {
+ using ImageAttributes imageAttributes = new ImageAttributes();
+ imageAttributes.ClearColorMatrix();
+ imageAttributes.SetColorMatrix(colorMatrix);
+ ApplyImageAttributes(source, sourceRect, dest, destRect, imageAttributes);
+ }
+
+ ///
+ /// Apply image attributes to the image
+ ///
+ /// Image to apply matrix to
+ /// ImageAttributes to apply
+ public static void ApplyColorMatrix(Bitmap source, ImageAttributes imageAttributes)
+ {
+ ApplyImageAttributes(source, Rectangle.Empty, source, Rectangle.Empty, imageAttributes);
+ }
+
+ ///
+ /// Apply a color matrix by copying from the source to the destination
+ ///
+ /// Image to copy from
+ /// Rectangle to copy from
+ /// Rectangle to copy to
+ /// Image to copy to
+ /// ImageAttributes to apply
+ public static void ApplyImageAttributes(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ImageAttributes imageAttributes)
+ {
+ if (sourceRect == Rectangle.Empty)
+ {
+ sourceRect = new Rectangle(0, 0, source.Width, source.Height);
+ }
+ if (dest == null)
+ {
+ dest = source;
+ }
+ if (destRect == Rectangle.Empty)
+ {
+ destRect = new Rectangle(0, 0, dest.Width, dest.Height);
+ }
+
+ using Graphics graphics = Graphics.FromImage(dest);
+ // Make sure we draw with the best quality!
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ graphics.CompositingMode = CompositingMode.SourceCopy;
+
+ graphics.DrawImage(source, destRect, sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height, GraphicsUnit.Pixel, imageAttributes);
+ }
+
+ ///
+ /// Returns a b/w of Bitmap
+ ///
+ /// Bitmap to create a b/w of
+ /// Threshold for monochrome filter (0 - 255), lower value means less black
+ /// b/w bitmap
+ public static Bitmap CreateMonochrome(Image sourceImage, byte threshold)
+ {
+ using IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat);
+ for (int y = 0; y < fastBitmap.Height; y++)
+ {
+ for (int x = 0; x < fastBitmap.Width; x++)
+ {
+ Color color = fastBitmap.GetColorAt(x, y);
+ int colorBrightness = (color.R + color.G + color.B) / 3 > threshold ? 255 : 0;
+ Color monoColor = Color.FromArgb(color.A, colorBrightness, colorBrightness, colorBrightness);
+ fastBitmap.SetColorAt(x, y, monoColor);
+ }
+ }
+ return fastBitmap.UnlockAndReturnBitmap();
+ }
+
+ ///
+ /// Create a new bitmap where the sourceBitmap has a Simple border around it
+ ///
+ /// Bitmap to make a border on
+ /// Size of the border
+ /// Color of the border
+ /// What pixel format must the returning bitmap have
+ /// The transform matrix which describes how the elements need to be transformed to stay at the same location
+ /// Bitmap with the shadow, is bigger than the sourceBitmap!!
+ public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, Matrix matrix)
+ {
+ // "return" the shifted offset, so the caller can e.g. move elements
+ Point offset = new Point(borderSize, borderSize);
+ matrix.Translate(offset.X, offset.Y, MatrixOrder.Append);
+
+ // Create a new "clean" image
+ Bitmap newImage = CreateEmpty(sourceImage.Width + borderSize * 2, sourceImage.Height + borderSize * 2, targetPixelformat, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ using (Graphics graphics = Graphics.FromImage(newImage))
+ {
+ // Make sure we draw with the best quality!
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ using (GraphicsPath path = new GraphicsPath())
+ {
+ path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - borderSize, newImage.Height - borderSize));
+ using Pen pen = new Pen(borderColor, borderSize)
+ {
+ LineJoin = LineJoin.Round,
+ StartCap = LineCap.Round,
+ EndCap = LineCap.Round
+ };
+ graphics.DrawPath(pen, path);
+ }
+ // draw original with a TextureBrush so we have nice antialiasing!
+ using Brush textureBrush = new TextureBrush(sourceImage, WrapMode.Clamp);
+ // We need to do a translate-tranform otherwise the image is wrapped
+ graphics.TranslateTransform(offset.X, offset.Y);
+ graphics.FillRectangle(textureBrush, 0, 0, sourceImage.Width, sourceImage.Height);
+ }
+ return newImage;
+ }
+
+ ///
+ /// Create ImageAttributes to modify
+ ///
+ ///
+ ///
+ ///
+ /// ImageAttributes
+ public static ImageAttributes CreateAdjustAttributes(float brightness, float contrast, float gamma)
+ {
+ float adjustedBrightness = brightness - 1.0f;
+ ColorMatrix applyColorMatrix = new ColorMatrix(
+ new[]
+ {
+ new[] {contrast, 0, 0, 0, 0}, // scale red
+ new[] {0, contrast, 0, 0, 0}, // scale green
+ new[] {0, 0, contrast, 0, 0}, // scale blue
+ new[] {0, 0, 0, 1.0f, 0}, // don't scale alpha
+ new[] {adjustedBrightness, adjustedBrightness, adjustedBrightness, 0, 1}
+ });
+
+ //create some image attributes
+ ImageAttributes attributes = new ImageAttributes();
+ attributes.ClearColorMatrix();
+ attributes.SetColorMatrix(applyColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+ attributes.SetGamma(gamma, ColorAdjustType.Bitmap);
+ return attributes;
+ }
+
+ ///
+ /// Adjust the brightness, contract or gamma of an image.
+ /// Use the value "1.0f" for no changes.
+ ///
+ /// Original bitmap
+ ///
+ ///
+ ///
+ /// Bitmap with grayscale
+ public static Image Adjust(Image sourceImage, float brightness, float contrast, float gamma)
+ {
+ //create a blank bitmap the same size as original
+ // If using 8bpp than the following exception comes: A Graphics object cannot be created from an image that has an indexed pixel format.
+ Bitmap newBitmap = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format24bppRgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ using (ImageAttributes adjustAttributes = CreateAdjustAttributes(brightness, contrast, gamma))
+ {
+ ApplyImageAttributes((Bitmap)sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, adjustAttributes);
+ }
+ return newBitmap;
+ }
+
+ ///
+ /// Create a new bitmap where the sourceBitmap is in grayscale
+ ///
+ /// Original bitmap
+ /// Bitmap with grayscale
+ public static Image CreateGrayscale(Image sourceImage)
+ {
+ Bitmap clone = (Bitmap)Clone(sourceImage);
+ ColorMatrix grayscaleMatrix = new ColorMatrix(new[]
+ {
+ new[] {.3f, .3f, .3f, 0, 0},
+ new[] {.59f, .59f, .59f, 0, 0},
+ new[] {.11f, .11f, .11f, 0, 0},
+ new float[] {0, 0, 0, 1, 0},
+ new float[] {0, 0, 0, 0, 1}
+ });
+ ApplyColorMatrix(clone, grayscaleMatrix);
+ return clone;
+ }
+
+ ///
+ /// Checks if the supplied Bitmap has a PixelFormat we support
+ ///
+ /// bitmap to check
+ /// bool if we support it
+ public static bool SupportsPixelFormat(Image image)
+ {
+ return SupportsPixelFormat(image.PixelFormat);
+ }
+
+ ///
+ /// Checks if we support the pixel format
+ ///
+ /// PixelFormat to check
+ /// bool if we support it
+ public static bool SupportsPixelFormat(PixelFormat pixelformat)
+ {
+ return pixelformat.Equals(PixelFormat.Format32bppArgb) ||
+ pixelformat.Equals(PixelFormat.Format32bppPArgb) ||
+ pixelformat.Equals(PixelFormat.Format32bppRgb) ||
+ pixelformat.Equals(PixelFormat.Format24bppRgb);
+ }
+
+ ///
+ /// Wrapper for just cloning which calls the CloneArea
+ ///
+ /// Image to clone
+ /// Bitmap with clone image data
+ public static Image Clone(Image sourceImage)
+ {
+ if (sourceImage is Metafile)
+ {
+ return (Image)sourceImage.Clone();
+ }
+ return CloneArea(sourceImage, Rectangle.Empty, PixelFormat.DontCare);
+ }
+
+ ///
+ /// Wrapper for just cloning & TargetFormat which calls the CloneArea
+ ///
+ /// Image to clone
+ /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)
+ /// Bitmap with clone image data
+ public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat)
+ {
+ return CloneArea(sourceBitmap, Rectangle.Empty, targetFormat);
+ }
+
+ ///
+ /// Clone an image, taking some rules into account:
+ /// 1) When sourceRect is the whole bitmap there is a GDI+ bug in Clone
+ /// Clone will than return the same PixelFormat as the source
+ /// a quick workaround is using new Bitmap which uses a default of Format32bppArgb
+ /// 2) When going from a transparent to a non transparent bitmap, we draw the background white!
+ ///
+ /// Source bitmap to clone
+ /// Rectangle to copy from the source, use Rectangle.Empty for all
+ /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)
+ ///
+ public static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat)
+ {
+ Bitmap newImage;
+ Rectangle bitmapRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
+
+ // Make sure the source is not Rectangle.Empty
+ if (Rectangle.Empty.Equals(sourceRect))
+ {
+ sourceRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
+ }
+ else
+ {
+ sourceRect.Intersect(bitmapRect);
+ }
+
+ // If no pixelformat is supplied
+ if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat)
+ {
+ if (SupportsPixelFormat(sourceImage.PixelFormat))
+ {
+ targetFormat = sourceImage.PixelFormat;
+ }
+ else if (Image.IsAlphaPixelFormat(sourceImage.PixelFormat))
+ {
+ targetFormat = PixelFormat.Format32bppArgb;
+ }
+ else
+ {
+ targetFormat = PixelFormat.Format24bppRgb;
+ }
+ }
+
+ // check the target format
+ if (!SupportsPixelFormat(targetFormat))
+ {
+ targetFormat = Image.IsAlphaPixelFormat(targetFormat) ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb;
+ }
+
+ bool destinationIsTransparent = Image.IsAlphaPixelFormat(targetFormat);
+ bool sourceIsTransparent = Image.IsAlphaPixelFormat(sourceImage.PixelFormat);
+ bool fromTransparentToNon = !destinationIsTransparent && sourceIsTransparent;
+ bool isBitmap = sourceImage is Bitmap;
+ bool isAreaEqual = sourceRect.Equals(bitmapRect);
+ if (isAreaEqual || fromTransparentToNon || !isBitmap)
+ {
+ // Rule 1: if the areas are equal, always copy ourselves
+ newImage = new Bitmap(bitmapRect.Width, bitmapRect.Height, targetFormat);
+ // Make sure both images have the same resolution
+ newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+
+ using Graphics graphics = Graphics.FromImage(newImage);
+ if (fromTransparentToNon)
+ {
+ // Rule 2: Make sure the background color is white
+ graphics.Clear(Color.White);
+ }
+ // decide fastest copy method
+ if (isAreaEqual)
+ {
+ graphics.DrawImageUnscaled(sourceImage, 0, 0);
+ }
+ else
+ {
+ graphics.DrawImage(sourceImage, 0, 0, sourceRect, GraphicsUnit.Pixel);
+ }
+ }
+ else
+ {
+ // Let GDI+ decide how to convert, need to test what is quicker...
+ newImage = (sourceImage as Bitmap).Clone(sourceRect, targetFormat);
+ // Make sure both images have the same resolution
+ newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ }
+ // Clone property items (EXIF information etc)
+ foreach (var propertyItem in sourceImage.PropertyItems)
+ {
+ try
+ {
+ newImage.SetPropertyItem(propertyItem);
+ }
+ catch (Exception ex)
+ {
+ Log.Warn("Problem cloning a propertyItem.", ex);
+ }
+ }
+ return newImage;
+ }
+
+ ///
+ /// Rotate the bitmap
+ ///
+ ///
+ ///
+ ///
+ public static Image RotateFlip(Image sourceImage, RotateFlipType rotateFlipType)
+ {
+ Image returnImage = Clone(sourceImage);
+ returnImage.RotateFlip(rotateFlipType);
+ return returnImage;
+ }
+
+ ///
+ /// A generic way to create an empty image
+ ///
+ /// the source bitmap as the specifications for the new bitmap
+ /// The color to fill with, or Color.Empty to take the default depending on the pixel format
+ ///
+ public static Bitmap CreateEmptyLike(Image sourceImage, Color backgroundColor)
+ {
+ PixelFormat pixelFormat = sourceImage.PixelFormat;
+ if (backgroundColor.A < 255)
+ {
+ pixelFormat = PixelFormat.Format32bppArgb;
+ }
+ return CreateEmpty(sourceImage.Width, sourceImage.Height, pixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ }
+
+ ///
+ /// A generic way to create an empty image
+ ///
+ ///
+ ///
+ ///
+ /// The color to fill with, or Color.Empty to take the default depending on the pixel format
+ ///
+ ///
+ /// Bitmap
+ public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution)
+ {
+ // Create a new "clean" image
+ Bitmap newImage = new Bitmap(width, height, format);
+ newImage.SetResolution(horizontalResolution, verticalResolution);
+ if (format != PixelFormat.Format8bppIndexed)
+ {
+ using Graphics graphics = Graphics.FromImage(newImage);
+ // Make sure the background color is what we want (transparent or white, depending on the pixel format)
+ if (!Color.Empty.Equals(backgroundColor))
+ {
+ graphics.Clear(backgroundColor);
+ }
+ else if (Image.IsAlphaPixelFormat(format))
+ {
+ graphics.Clear(Color.Transparent);
+ }
+ else
+ {
+ graphics.Clear(Color.White);
+ }
+ }
+ return newImage;
+ }
+
+ ///
+ /// Get a scaled version of the sourceBitmap
+ ///
+ ///
+ /// 1-99 to make smaller, use 101 and more to make the picture bigger
+ ///
+ public static Bitmap ScaleByPercent(Bitmap sourceBitmap, int percent)
+ {
+ float nPercent = (float)percent / 100;
+
+ int sourceWidth = sourceBitmap.Width;
+ int sourceHeight = sourceBitmap.Height;
+ int destWidth = (int)(sourceWidth * nPercent);
+ int destHeight = (int)(sourceHeight * nPercent);
+
+ Bitmap scaledBitmap = CreateEmpty(destWidth, destHeight, sourceBitmap.PixelFormat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
+ using (Graphics graphics = Graphics.FromImage(scaledBitmap))
+ {
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, destWidth, destHeight), new Rectangle(0, 0, sourceWidth, sourceHeight), GraphicsUnit.Pixel);
+ }
+ return scaledBitmap;
+ }
+
+ ///
+ /// Resize canvas with pixel to the left, right, top and bottom
+ ///
+ ///
+ /// The color to fill with, or Color.Empty to take the default depending on the pixel format
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// a new bitmap with the source copied on it
+ public static Image ResizeCanvas(Image sourceImage, Color backgroundColor, int left, int right, int top, int bottom, Matrix matrix)
+ {
+ matrix.Translate(left, top, MatrixOrder.Append);
+ Bitmap newBitmap = CreateEmpty(sourceImage.Width + left + right, sourceImage.Height + top + bottom, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ using (Graphics graphics = Graphics.FromImage(newBitmap))
+ {
+ graphics.DrawImageUnscaled(sourceImage, left, top);
+ }
+ return newBitmap;
+ }
+
+ ///
+ /// Wrapper for the more complex Resize, this resize could be used for e.g. Thumbnails
+ ///
+ ///
+ /// true to maintain the aspect ratio
+ ///
+ ///
+ ///
+ ///
+ public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, int newWidth, int newHeight, Matrix matrix)
+ {
+ return ResizeImage(sourceImage, maintainAspectRatio, false, Color.Empty, newWidth, newHeight, matrix);
+ }
+
+ ///
+ /// Count how many times the supplied color exists
+ ///
+ /// Image to count the pixels of
+ /// Color to count
+ /// true if Alpha needs to be checked
+ /// int with the number of pixels which have colorToCount
+ public static int CountColor(Image sourceImage, Color colorToCount, bool includeAlpha)
+ {
+ int colors = 0;
+ int toCount = colorToCount.ToArgb();
+ if (!includeAlpha)
+ {
+ toCount &= 0xffffff;
+ }
+
+ using IFastBitmap bb = FastBitmap.Create((Bitmap)sourceImage);
+ for (int y = 0; y < bb.Height; y++)
+ {
+ for (int x = 0; x < bb.Width; x++)
+ {
+ int bitmapcolor = bb.GetColorAt(x, y).ToArgb();
+ if (!includeAlpha)
+ {
+ bitmapcolor &= 0xffffff;
+ }
+ if (bitmapcolor == toCount)
+ {
+ colors++;
+ }
+ }
+ }
+ return colors;
+ }
+
+ ///
+ /// Scale the bitmap, keeping aspect ratio, but the canvas will always have the specified size.
+ ///
+ /// Image to scale
+ /// true to maintain the aspect ratio
+ /// Makes the image maintain aspect ratio, but the canvas get's the specified size
+ /// The color to fill with, or Color.Empty to take the default depending on the pixel format
+ /// new width
+ /// new height
+ ///
+ /// a new bitmap with the specified size, the source-Image scaled to fit with aspect ratio locked
+ public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, bool canvasUseNewSize, Color backgroundColor, int newWidth, int newHeight, Matrix matrix)
+ {
+ int destX = 0;
+ int destY = 0;
+
+ var nPercentW = newWidth / (float)sourceImage.Width;
+ var nPercentH = newHeight / (float)sourceImage.Height;
+ if (maintainAspectRatio)
+ {
+ if ((int)nPercentW == 1)
+ {
+ nPercentW = nPercentH;
+ if (canvasUseNewSize)
+ {
+ destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2));
+ }
+ }
+ else if ((int)nPercentH == 1)
+ {
+ nPercentH = nPercentW;
+ if (canvasUseNewSize)
+ {
+ destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2));
+ }
+ }
+ else if ((int)nPercentH != 0 && nPercentH < nPercentW)
+ {
+ nPercentW = nPercentH;
+ if (canvasUseNewSize)
+ {
+ destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2));
+ }
+ }
+ else
+ {
+ nPercentH = nPercentW;
+ if (canvasUseNewSize)
+ {
+ destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2));
+ }
+ }
+ }
+
+ int destWidth = (int)(sourceImage.Width * nPercentW);
+ int destHeight = (int)(sourceImage.Height * nPercentH);
+ if (newWidth == 0)
+ {
+ newWidth = destWidth;
+ }
+ if (newHeight == 0)
+ {
+ newHeight = destHeight;
+ }
+ Image newImage;
+ if (maintainAspectRatio && canvasUseNewSize)
+ {
+ newImage = CreateEmpty(newWidth, newHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ matrix?.Scale((float)newWidth / sourceImage.Width, (float)newHeight / sourceImage.Height, MatrixOrder.Append);
+ }
+ else
+ {
+ newImage = CreateEmpty(destWidth, destHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ matrix?.Scale((float)destWidth / sourceImage.Width, (float)destHeight / sourceImage.Height, MatrixOrder.Append);
+ }
+
+ using (Graphics graphics = Graphics.FromImage(newImage))
+ {
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ using ImageAttributes wrapMode = new ImageAttributes();
+ wrapMode.SetWrapMode(WrapMode.TileFlipXY);
+ graphics.DrawImage(sourceImage, new Rectangle(destX, destY, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, wrapMode);
+ }
+ return newImage;
+ }
+
+ ///
+ /// Load a Greenshot surface from a stream
+ ///
+ /// Stream
+ ///
+ /// ISurface
+ public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface)
+ {
+ Image fileImage;
+ // Fixed problem that the bitmap stream is disposed... by Cloning the image
+ // This also ensures the bitmap is correctly created
+
+ // We create a copy of the bitmap, so everything else can be disposed
+ surfaceFileStream.Position = 0;
+ using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true))
+ {
+ Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
+ fileImage = Clone(tmpImage);
+ }
+ // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor)
+ const int markerSize = 14;
+ surfaceFileStream.Seek(-markerSize, SeekOrigin.End);
+ using (StreamReader streamReader = new StreamReader(surfaceFileStream))
+ {
+ var greenshotMarker = streamReader.ReadToEnd();
+ if (!greenshotMarker.StartsWith("Greenshot"))
+ {
+ throw new ArgumentException("Stream is not a Greenshot file!");
+ }
+ Log.InfoFormat("Greenshot file format: {0}", greenshotMarker);
+ const int filesizeLocation = 8 + markerSize;
+ surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End);
+ using BinaryReader reader = new BinaryReader(surfaceFileStream);
+ long bytesWritten = reader.ReadInt64();
+ surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End);
+ returnSurface.LoadElementsFromStream(surfaceFileStream);
+ }
+ if (fileImage != null)
+ {
+ returnSurface.Image = fileImage;
+ Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat, fileImage.HorizontalResolution, fileImage.VerticalResolution);
+ }
+ return returnSurface;
+ }
+
+ ///
+ /// Create an image from a stream, if an extension is supplied more formats are supported.
+ ///
+ /// Stream
+ ///
+ /// Image
+ public static Image FromStream(Stream stream, string extension = null)
+ {
+ if (stream == null)
+ {
+ return null;
+ }
+ if (!string.IsNullOrEmpty(extension))
+ {
+ extension = extension.Replace(".", string.Empty);
+ }
+
+ // Make sure we can try multiple times
+ if (!stream.CanSeek)
+ {
+ var memoryStream = new MemoryStream();
+ stream.CopyTo(memoryStream);
+ stream = memoryStream;
+ }
+
+ Image returnImage = null;
+ if (StreamConverters.TryGetValue(extension ?? string.Empty, out var converter))
+ {
+ returnImage = converter(stream, extension);
+ }
+ // Fallback
+ if (returnImage == null)
+ {
+ // We create a copy of the bitmap, so everything else can be disposed
+ stream.Position = 0;
+ using var tmpImage = Image.FromStream(stream, true, true);
+ Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
+ returnImage = Clone(tmpImage, PixelFormat.Format32bppArgb);
+ }
+ return returnImage;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/ImageOutput.cs b/GreenshotPlugin/Core/ImageOutput.cs
new file mode 100644
index 000000000..97c8f8cf3
--- /dev/null
+++ b/GreenshotPlugin/Core/ImageOutput.cs
@@ -0,0 +1,657 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using GreenshotPlugin.Controls;
+using log4net;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using GreenshotPlugin.IniFile;
+using GreenshotPlugin.Interfaces;
+using GreenshotPlugin.Interfaces.Plugin;
+using Encoder = System.Drawing.Imaging.Encoder;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Description of ImageOutput.
+ ///
+ public static class ImageOutput {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ImageOutput));
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131;
+ private static readonly Cache TmpFileCache = new Cache(10 * 60 * 60, RemoveExpiredTmpFile);
+
+ ///
+ /// Creates a PropertyItem (Metadata) to store with the image.
+ /// For the possible ID's see: http://msdn.microsoft.com/de-de/library/system.drawing.imaging.propertyitem.id(v=vs.80).aspx
+ /// This code uses Reflection to create a PropertyItem, although it's not adviced it's not as stupid as having a image in the project so we can read a PropertyItem from that!
+ ///
+ /// ID
+ /// Text
+ ///
+ private static PropertyItem CreatePropertyItem(int id, string text) {
+ PropertyItem propertyItem = null;
+ try {
+ ConstructorInfo ci = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new Type[] { }, null);
+ propertyItem = (PropertyItem)ci.Invoke(null);
+ // Make sure it's of type string
+ propertyItem.Type = 2;
+ // Set the ID
+ propertyItem.Id = id;
+ // Set the text
+ byte[] byteString = Encoding.ASCII.GetBytes(text + " ");
+ // Set Zero byte for String end.
+ byteString[byteString.Length - 1] = 0;
+ propertyItem.Value = byteString;
+ propertyItem.Len = text.Length + 1;
+ } catch (Exception e) {
+ Log.WarnFormat("Error creating a PropertyItem: {0}", e.Message);
+ }
+ return propertyItem;
+ }
+
+ ///
+ /// Saves ISurface to stream with specified output settings
+ ///
+ /// ISurface to save
+ /// Stream to save to
+ /// SurfaceOutputSettings
+ public static void SaveToStream(ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) {
+ bool disposeImage = CreateImageFromSurface(surface, outputSettings, out var imageToSave);
+ SaveToStream(imageToSave, surface, stream, outputSettings);
+ // cleanup if needed
+ if (disposeImage) {
+ imageToSave?.Dispose();
+ }
+ }
+
+ ///
+ /// Saves image to stream with specified quality
+ /// To prevent problems with GDI version of before Windows 7:
+ /// the stream is checked if it's seekable and if needed a MemoryStream as "cache" is used.
+ ///
+ /// image to save
+ /// surface for the elements, needed if the greenshot format is used
+ /// Stream to save to
+ /// SurfaceOutputSettings
+ public static void SaveToStream(Image imageToSave, ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) {
+ bool useMemoryStream = false;
+ MemoryStream memoryStream = null;
+ if (outputSettings.Format == OutputFormat.greenshot && surface == null) {
+ throw new ArgumentException("Surface needs to be set when using OutputFormat.Greenshot");
+ }
+
+ try {
+ var imageFormat = outputSettings.Format switch
+ {
+ OutputFormat.bmp => ImageFormat.Bmp,
+ OutputFormat.gif => ImageFormat.Gif,
+ OutputFormat.jpg => ImageFormat.Jpeg,
+ OutputFormat.tiff => ImageFormat.Tiff,
+ OutputFormat.ico => ImageFormat.Icon,
+ _ => ImageFormat.Png
+ };
+ Log.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat);
+
+ // Check if we want to use a memory stream, to prevent issues with non seakable streams
+ // The save is made to the targetStream, this is directed to either the MemoryStream or the original
+ Stream targetStream = stream;
+ if (!stream.CanSeek)
+ {
+ useMemoryStream = true;
+ Log.Warn("Using memorystream prevent an issue with saving to a non seekable stream.");
+ memoryStream = new MemoryStream();
+ targetStream = memoryStream;
+ }
+
+ if (Equals(imageFormat, ImageFormat.Jpeg))
+ {
+ bool foundEncoder = false;
+ foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders())
+ {
+ if (imageCodec.FormatID == imageFormat.Guid)
+ {
+ EncoderParameters parameters = new EncoderParameters(1)
+ {
+ Param = {[0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality)}
+ };
+ // Removing transparency if it's not supported in the output
+ if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat))
+ {
+ Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
+ AddTag(nonAlphaImage);
+ nonAlphaImage.Save(targetStream, imageCodec, parameters);
+ nonAlphaImage.Dispose();
+ }
+ else
+ {
+ AddTag(imageToSave);
+ imageToSave.Save(targetStream, imageCodec, parameters);
+ }
+ foundEncoder = true;
+ break;
+ }
+ }
+ if (!foundEncoder)
+ {
+ throw new ApplicationException("No JPG encoder found, this should not happen.");
+ }
+ } else if (Equals(imageFormat, ImageFormat.Icon)) {
+ // FEATURE-916: Added Icon support
+ IList images = new List
+ {
+ imageToSave
+ };
+ WriteIcon(stream, images);
+ } else {
+ bool needsDispose = false;
+ // Removing transparency if it's not supported in the output
+ if (!Equals(imageFormat, ImageFormat.Png) && Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) {
+ imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
+ needsDispose = true;
+ }
+ AddTag(imageToSave);
+ // Added for OptiPNG
+ bool processed = false;
+ if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand)) {
+ processed = ProcessPngImageExternally(imageToSave, targetStream);
+ }
+ if (!processed) {
+ imageToSave.Save(targetStream, imageFormat);
+ }
+ if (needsDispose) {
+ imageToSave.Dispose();
+ }
+ }
+
+ // If we used a memory stream, we need to stream the memory stream to the original stream.
+ if (useMemoryStream) {
+ memoryStream.WriteTo(stream);
+ }
+
+ // Output the surface elements, size and marker to the stream
+ if (outputSettings.Format != OutputFormat.greenshot)
+ {
+ return;
+ }
+
+ using MemoryStream tmpStream = new MemoryStream();
+ long bytesWritten = surface.SaveElementsToStream(tmpStream);
+ using BinaryWriter writer = new BinaryWriter(tmpStream);
+ writer.Write(bytesWritten);
+ Version v = Assembly.GetExecutingAssembly().GetName().Version;
+ byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}");
+ writer.Write(marker);
+ tmpStream.WriteTo(stream);
+ }
+ finally
+ {
+ memoryStream?.Dispose();
+ }
+ }
+
+ ///
+ /// Write the passed Image to a tmp-file and call an external process, than read the file back and write it to the targetStream
+ ///
+ /// Image to pass to the external process
+ /// stream to write the processed image to
+ ///
+ private static bool ProcessPngImageExternally(Image imageToProcess, Stream targetStream) {
+ if (string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand)) {
+ return false;
+ }
+ if (!File.Exists(CoreConfig.OptimizePNGCommand)) {
+ Log.WarnFormat("Can't find 'OptimizePNGCommand' {0}", CoreConfig.OptimizePNGCommand);
+ return false;
+ }
+ string tmpFileName = Path.Combine(Path.GetTempPath(),Path.GetRandomFileName() + ".png");
+ try {
+ using (FileStream tmpStream = File.Create(tmpFileName)) {
+ Log.DebugFormat("Writing png to tmp file: {0}", tmpFileName);
+ imageToProcess.Save(tmpStream, ImageFormat.Png);
+ if (Log.IsDebugEnabled) {
+ Log.DebugFormat("File size before processing {0}", new FileInfo(tmpFileName).Length);
+ }
+ }
+ if (Log.IsDebugEnabled) {
+ Log.DebugFormat("Starting : {0}", CoreConfig.OptimizePNGCommand);
+ }
+
+ ProcessStartInfo processStartInfo = new ProcessStartInfo(CoreConfig.OptimizePNGCommand)
+ {
+ Arguments = string.Format(CoreConfig.OptimizePNGCommandArguments, tmpFileName),
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false
+ };
+ using Process process = Process.Start(processStartInfo);
+ if (process != null) {
+ process.WaitForExit();
+ if (process.ExitCode == 0) {
+ if (Log.IsDebugEnabled) {
+ Log.DebugFormat("File size after processing {0}", new FileInfo(tmpFileName).Length);
+ Log.DebugFormat("Reading back tmp file: {0}", tmpFileName);
+ }
+ byte[] processedImage = File.ReadAllBytes(tmpFileName);
+ targetStream.Write(processedImage, 0, processedImage.Length);
+ return true;
+ }
+ Log.ErrorFormat("Error while processing PNG image: {0}", process.ExitCode);
+ Log.ErrorFormat("Output: {0}", process.StandardOutput.ReadToEnd());
+ Log.ErrorFormat("Error: {0}", process.StandardError.ReadToEnd());
+ }
+ } catch (Exception e) {
+ Log.Error("Error while processing PNG image: ", e);
+ } finally {
+ if (File.Exists(tmpFileName)) {
+ Log.DebugFormat("Cleaning up tmp file: {0}", tmpFileName);
+ File.Delete(tmpFileName);
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Create an image from a surface with the settings from the output settings applied
+ ///
+ ///
+ ///
+ ///
+ /// true if the image must be disposed
+ public static bool CreateImageFromSurface(ISurface surface, SurfaceOutputSettings outputSettings, out Image imageToSave) {
+ bool disposeImage = false;
+
+ if (outputSettings.Format == OutputFormat.greenshot || outputSettings.SaveBackgroundOnly) {
+ // We save the image of the surface, this should not be disposed
+ imageToSave = surface.Image;
+ } else {
+ // We create the export image of the surface to save
+ imageToSave = surface.GetImageForExport();
+ disposeImage = true;
+ }
+
+ // The following block of modifications should be skipped when saving the greenshot format, no effects or otherwise!
+ if (outputSettings.Format == OutputFormat.greenshot) {
+ return disposeImage;
+ }
+ Image tmpImage;
+ if (outputSettings.Effects != null && outputSettings.Effects.Count > 0) {
+ // apply effects, if there are any
+ using (Matrix matrix = new Matrix()) {
+ tmpImage = ImageHelper.ApplyEffects(imageToSave, outputSettings.Effects, matrix);
+ }
+ if (tmpImage != null) {
+ if (disposeImage) {
+ imageToSave.Dispose();
+ }
+ imageToSave = tmpImage;
+ disposeImage = true;
+ }
+ }
+
+ // check for color reduction, forced or automatically, only when the DisableReduceColors is false
+ if (outputSettings.DisableReduceColors || (!CoreConfig.OutputFileAutoReduceColors && !outputSettings.ReduceColors)) {
+ return disposeImage;
+ }
+ bool isAlpha = Image.IsAlphaPixelFormat(imageToSave.PixelFormat);
+ if (outputSettings.ReduceColors || (!isAlpha && CoreConfig.OutputFileAutoReduceColors))
+ {
+ using var quantizer = new WuQuantizer((Bitmap)imageToSave);
+ int colorCount = quantizer.GetColorCount();
+ Log.InfoFormat("Image with format {0} has {1} colors", imageToSave.PixelFormat, colorCount);
+ if (!outputSettings.ReduceColors && colorCount >= 256) {
+ return disposeImage;
+ }
+ try {
+ Log.Info("Reducing colors on bitmap to 256.");
+ tmpImage = quantizer.GetQuantizedImage(CoreConfig.OutputFileReduceColorsTo);
+ if (disposeImage) {
+ imageToSave.Dispose();
+ }
+ imageToSave = tmpImage;
+ // Make sure the "new" image is disposed
+ disposeImage = true;
+ } catch (Exception e) {
+ Log.Warn("Error occurred while Quantizing the image, ignoring and using original. Error: ", e);
+ }
+ } else if (isAlpha && !outputSettings.ReduceColors) {
+ Log.Info("Skipping 'optional' color reduction as the image has alpha");
+ }
+ return disposeImage;
+ }
+
+ ///
+ /// Add the greenshot property!
+ ///
+ ///
+ private static void AddTag(Image imageToSave) {
+ // Create meta-data
+ PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot");
+ if (softwareUsedPropertyItem != null) {
+ try {
+ imageToSave.SetPropertyItem(softwareUsedPropertyItem);
+ } catch (Exception) {
+ Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id);
+ }
+ }
+ }
+
+ ///
+ /// Load a Greenshot surface
+ ///
+ ///
+ ///
+ ///
+ public static ISurface LoadGreenshotSurface(string fullPath, ISurface returnSurface) {
+ if (string.IsNullOrEmpty(fullPath)) {
+ return null;
+ }
+ Log.InfoFormat("Loading image from file {0}", fullPath);
+ // Fixed lock problem Bug #3431881
+ using (Stream surfaceFileStream = File.OpenRead(fullPath)) {
+ returnSurface = ImageHelper.LoadGreenshotSurface(surfaceFileStream, returnSurface);
+ }
+ if (returnSurface != null) {
+ Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, returnSurface.Image.Width, returnSurface.Image.Height, returnSurface.Image.PixelFormat, returnSurface.Image.HorizontalResolution, returnSurface.Image.VerticalResolution);
+ }
+ return returnSurface;
+ }
+
+ ///
+ /// Saves image to specific path with specified quality
+ ///
+ public static void Save(ISurface surface, string fullPath, bool allowOverwrite, SurfaceOutputSettings outputSettings, bool copyPathToClipboard) {
+ fullPath = FilenameHelper.MakeFqFilenameSafe(fullPath);
+ string path = Path.GetDirectoryName(fullPath);
+
+ // check whether path exists - if not create it
+ if (path != null) {
+ DirectoryInfo di = new DirectoryInfo(path);
+ if (!di.Exists) {
+ Directory.CreateDirectory(di.FullName);
+ }
+ }
+
+ if (!allowOverwrite && File.Exists(fullPath)) {
+ ArgumentException throwingException = new ArgumentException("File '" + fullPath + "' already exists.");
+ throwingException.Data.Add("fullPath", fullPath);
+ throw throwingException;
+ }
+ Log.DebugFormat("Saving surface to {0}", fullPath);
+ // Create the stream and call SaveToStream
+ using (FileStream stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write)) {
+ SaveToStream(surface, stream, outputSettings);
+ }
+
+ if (copyPathToClipboard) {
+ ClipboardHelper.SetClipboardData(fullPath);
+ }
+ }
+
+ ///
+ /// Get the OutputFormat for a filename
+ ///
+ /// filename (can be a complete path)
+ /// OutputFormat
+ public static OutputFormat FormatForFilename(string fullPath) {
+ // Fix for bug 2912959
+ string extension = fullPath.Substring(fullPath.LastIndexOf(".", StringComparison.Ordinal) + 1);
+ OutputFormat format = OutputFormat.png;
+ try {
+ format = (OutputFormat)Enum.Parse(typeof(OutputFormat), extension.ToLower());
+ } catch (ArgumentException ae) {
+ Log.Warn("Couldn't parse extension: " + extension, ae);
+ }
+ return format;
+ }
+
+ ///
+ /// Save with showing a dialog
+ ///
+ ///
+ ///
+ /// Path to filename
+ public static string SaveWithDialog(ISurface surface, ICaptureDetails captureDetails) {
+ string returnValue = null;
+ using (SaveImageFileDialog saveImageFileDialog = new SaveImageFileDialog(captureDetails)) {
+ DialogResult dialogResult = saveImageFileDialog.ShowDialog();
+ if (dialogResult.Equals(DialogResult.OK)) {
+ try {
+ string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension;
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension));
+ if (CoreConfig.OutputFilePromptQuality) {
+ QualityDialog qualityDialog = new QualityDialog(outputSettings);
+ qualityDialog.ShowDialog();
+ }
+ // TODO: For now we always overwrite, should be changed
+ Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard);
+ returnValue = fileNameWithExtension;
+ IniConfig.Save();
+ } catch (ExternalException) {
+ MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error"));
+ }
+ }
+ }
+ return returnValue;
+ }
+
+ ///
+ /// Create a tmpfile which has the name like in the configured pattern.
+ /// Used e.g. by the email export
+ ///
+ ///
+ ///
+ ///
+ /// Path to image file
+ public static string SaveNamedTmpFile(ISurface surface, ICaptureDetails captureDetails, SurfaceOutputSettings outputSettings) {
+ string pattern = CoreConfig.OutputFileFilenamePattern;
+ if (string.IsNullOrEmpty(pattern?.Trim())) {
+ pattern = "greenshot ${capturetime}";
+ }
+ string filename = FilenameHelper.GetFilenameFromPattern(pattern, outputSettings.Format, captureDetails);
+ // Prevent problems with "other characters", which causes a problem in e.g. Outlook 2007 or break our HTML
+ filename = Regex.Replace(filename, @"[^\d\w\.]", "_");
+ // Remove multiple "_"
+ filename = Regex.Replace(filename, @"_+", "_");
+ string tmpFile = Path.Combine(Path.GetTempPath(), filename);
+
+ Log.Debug("Creating TMP File: " + tmpFile);
+
+ // Catching any exception to prevent that the user can't write in the directory.
+ // This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218
+ try {
+ Save(surface, tmpFile, true, outputSettings, false);
+ TmpFileCache.Add(tmpFile, tmpFile);
+ } catch (Exception e) {
+ // Show the problem
+ MessageBox.Show(e.Message, "Error");
+ // when save failed we present a SaveWithDialog
+ tmpFile = SaveWithDialog(surface, captureDetails);
+ }
+ return tmpFile;
+ }
+
+ ///
+ /// Remove a tmpfile which was created by SaveNamedTmpFile
+ /// Used e.g. by the email export
+ ///
+ ///
+ /// true if it worked
+ public static bool DeleteNamedTmpFile(string tmpfile) {
+ Log.Debug("Deleting TMP File: " + tmpfile);
+ try {
+ if (File.Exists(tmpfile)) {
+ File.Delete(tmpfile);
+ TmpFileCache.Remove(tmpfile);
+ }
+ return true;
+ } catch (Exception ex) {
+ Log.Warn("Error deleting tmp file: ", ex);
+ }
+ return false;
+ }
+
+ ///
+ /// Helper method to create a temp image file
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string SaveToTmpFile(ISurface surface, SurfaceOutputSettings outputSettings, string destinationPath) {
+ string tmpFile = Path.GetRandomFileName() + "." + outputSettings.Format;
+ // Prevent problems with "other characters", which could cause problems
+ tmpFile = Regex.Replace(tmpFile, @"[^\d\w\.]", string.Empty);
+ if (destinationPath == null) {
+ destinationPath = Path.GetTempPath();
+ }
+ string tmpPath = Path.Combine(destinationPath, tmpFile);
+ Log.Debug("Creating TMP File : " + tmpPath);
+
+ try {
+ Save(surface, tmpPath, true, outputSettings, false);
+ TmpFileCache.Add(tmpPath, tmpPath);
+ } catch (Exception) {
+ return null;
+ }
+ return tmpPath;
+ }
+
+ ///
+ /// Cleanup all created tmpfiles
+ ///
+ public static void RemoveTmpFiles() {
+ foreach (string tmpFile in TmpFileCache.Elements) {
+ if (File.Exists(tmpFile)) {
+ Log.DebugFormat("Removing old temp file {0}", tmpFile);
+ File.Delete(tmpFile);
+ }
+ TmpFileCache.Remove(tmpFile);
+ }
+ }
+
+ ///
+ /// Cleanup handler for expired tempfiles
+ ///
+ ///
+ ///
+ private static void RemoveExpiredTmpFile(string filekey, object filename) {
+ if (filename is string path && File.Exists(path)) {
+ Log.DebugFormat("Removing expired file {0}", path);
+ File.Delete(path);
+ }
+ }
+
+ ///
+ /// Write the images to the stream as icon
+ /// Every image is resized to 256x256 (but the content maintains the aspect ratio)
+ ///
+ /// Stream to write to
+ /// List of images
+ public static void WriteIcon(Stream stream, IList images)
+ {
+ var binaryWriter = new BinaryWriter(stream);
+ //
+ // ICONDIR structure
+ //
+ binaryWriter.Write((short)0); // reserved
+ binaryWriter.Write((short)1); // image type (icon)
+ binaryWriter.Write((short)images.Count); // number of images
+
+ IList imageSizes = new List();
+ IList encodedImages = new List();
+ foreach (var image in images)
+ {
+ // Pick the best fit
+ var sizes = new[] { 16, 32, 48 };
+ int size = 256;
+ foreach (var possibleSize in sizes)
+ {
+ if (image.Width <= possibleSize && image.Height <= possibleSize)
+ {
+ size = possibleSize;
+ break;
+ }
+ }
+ var imageStream = new MemoryStream();
+ if (image.Width == size && image.Height == size)
+ {
+ using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
+ clonedImage.Save(imageStream, ImageFormat.Png);
+ imageSizes.Add(new Size(size, size));
+ }
+ else
+ {
+ // Resize to the specified size, first make sure the image is 32bpp
+ using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
+ using var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, size, size, null);
+ resizedImage.Save(imageStream, ImageFormat.Png);
+ imageSizes.Add(resizedImage.Size);
+ }
+
+ imageStream.Seek(0, SeekOrigin.Begin);
+ encodedImages.Add(imageStream);
+ }
+
+ //
+ // ICONDIRENTRY structure
+ //
+ const int iconDirSize = 6;
+ const int iconDirEntrySize = 16;
+
+ var offset = iconDirSize + (images.Count * iconDirEntrySize);
+ for (int i = 0; i < images.Count; i++)
+ {
+ var imageSize = imageSizes[i];
+ // Write the width / height, 0 means 256
+ binaryWriter.Write(imageSize.Width == 256 ? (byte)0 : (byte)imageSize.Width);
+ binaryWriter.Write(imageSize.Height == 256 ? (byte)0 : (byte)imageSize.Height);
+ binaryWriter.Write((byte)0); // no pallete
+ binaryWriter.Write((byte)0); // reserved
+ binaryWriter.Write((short)0); // no color planes
+ binaryWriter.Write((short)32); // 32 bpp
+ binaryWriter.Write((int)encodedImages[i].Length); // image data length
+ binaryWriter.Write(offset);
+ offset += (int)encodedImages[i].Length;
+ }
+
+ binaryWriter.Flush();
+ //
+ // Write image data
+ //
+ foreach (var encodedImage in encodedImages)
+ {
+ encodedImage.WriteTo(stream);
+ encodedImage.Dispose();
+ }
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/ImageWrapper.cs b/GreenshotPlugin/Core/ImageWrapper.cs
new file mode 100644
index 000000000..f4cc03247
--- /dev/null
+++ b/GreenshotPlugin/Core/ImageWrapper.cs
@@ -0,0 +1,86 @@
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace GreenshotPlugin.Core
+{
+ ///
+ /// Wrap an image, make it resizeable
+ ///
+ public class ImageWrapper : IImage
+ {
+ // Underlying image, is used to generate a resized version of it when needed
+ private readonly Image _image;
+ private Image _imageClone;
+
+ ///
+ /// Factory method
+ ///
+ /// Image
+ /// IImage
+ public static IImage FromImage(Image image)
+ {
+ return image == null ? null : new ImageWrapper(image);
+ }
+
+ public ImageWrapper(Image image)
+ {
+ // Make sure the orientation is set correctly so Greenshot can process the image correctly
+ ImageHelper.Orientate(image);
+ _image = image;
+ Width = _image.Width;
+ Height = _image.Height;
+ }
+
+ public void Dispose()
+ {
+ _image.Dispose();
+ _imageClone?.Dispose();
+ }
+
+ ///
+ /// Height of the image, can be set to change
+ ///
+ public int Height { get; set; }
+
+ ///
+ /// Width of the image, can be set to change.
+ ///
+ public int Width { get; set; }
+
+ ///
+ /// Size of the image
+ ///
+ public Size Size => new Size(Width, Height);
+
+ ///
+ /// Pixelformat of the underlying image
+ ///
+ public PixelFormat PixelFormat => Image.PixelFormat;
+
+ public float HorizontalResolution => Image.HorizontalResolution;
+ public float VerticalResolution => Image.VerticalResolution;
+
+ public Image Image
+ {
+ get
+ {
+ if (_imageClone == null)
+ {
+ if (_image.Height == Height && _image.Width == Width)
+ {
+ return _image;
+ }
+ }
+ if (_imageClone?.Height == Height && _imageClone?.Width == Width)
+ {
+ return _imageClone;
+ }
+ // Calculate new image clone
+ _imageClone?.Dispose();
+ _imageClone = ImageHelper.ResizeImage(_image, false, Width, Height, null);
+ return _imageClone;
+ }
+ }
+
+ }
+}
diff --git a/GreenshotPlugin/Core/InterfaceUtils.cs b/GreenshotPlugin/Core/InterfaceUtils.cs
new file mode 100644
index 000000000..b592474f0
--- /dev/null
+++ b/GreenshotPlugin/Core/InterfaceUtils.cs
@@ -0,0 +1,74 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading;
+using GreenshotPlugin.Interfaces;
+using log4net;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// Description of InterfaceUtils.
+ ///
+ public static class InterfaceUtils {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(InterfaceUtils));
+
+ public static List GetSubclassesOf(Type type, bool excludeSystemTypes) {
+ List list = new List();
+ foreach(Assembly currentAssembly in Thread.GetDomain().GetAssemblies()) {
+ try {
+ Type[] types = currentAssembly.GetTypes();
+ if (!excludeSystemTypes || (excludeSystemTypes && !currentAssembly.FullName.StartsWith("System."))) {
+ foreach(Type currentType in types) {
+ if (type.IsInterface) {
+ if (currentType.GetInterface(type.FullName) != null) {
+ list.Add(currentType);
+ }
+ } else if (currentType.IsSubclassOf(type)) {
+ list.Add(currentType);
+ }
+ }
+ }
+ } catch (Exception ex) {
+ LOG.WarnFormat("Problem getting subclasses of type: {0}, message: {1}", type.FullName, ex.Message);
+ }
+ }
+ return list;
+ }
+
+ public static List GetProcessors() {
+ List processors = new List();
+ foreach(Type processorType in GetSubclassesOf(typeof(IProcessor), true)) {
+ if (!processorType.IsAbstract) {
+ IProcessor processor = (IProcessor)Activator.CreateInstance(processorType);
+ if (processor.isActive) {
+ LOG.DebugFormat("Found processor {0} with designation {1}", processorType.Name, processor.Designation);
+ processors.Add(processor);
+ } else {
+ LOG.DebugFormat("Ignoring processor {0} with designation {1}", processorType.Name, processor.Designation);
+ }
+ }
+ }
+ return processors;
+ }
+ }
+}
diff --git a/GreenshotPlugin/Core/JSONHelper.cs b/GreenshotPlugin/Core/JSONHelper.cs
new file mode 100644
index 000000000..d02845b72
--- /dev/null
+++ b/GreenshotPlugin/Core/JSONHelper.cs
@@ -0,0 +1,359 @@
+/*
+Copyright (C) 2008 Patrick van Bergen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace GreenshotPlugin.Core {
+ ///
+ /// This parses a JSON response, a modified version of the code found at:
+ /// See: http://techblog.procurios.nl/k/n618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
+ ///
+ /// This file is under the MIT License, which is GPL Compatible and according to: http://en.wikipedia.org/wiki/MIT_License
+ /// can be used under the GPL "umbrella".
+ ///
+ /// TODO: code should be replaced when upgrading to .NET 3.5 or higher!!
+ ///
+ public class JSONHelper {
+ public const int TOKEN_NONE = 0;
+ public const int TOKEN_CURLY_OPEN = 1;
+ public const int TOKEN_CURLY_CLOSE = 2;
+ public const int TOKEN_SQUARED_OPEN = 3;
+ public const int TOKEN_SQUARED_CLOSE = 4;
+ public const int TOKEN_COLON = 5;
+ public const int TOKEN_COMMA = 6;
+ public const int TOKEN_STRING = 7;
+ public const int TOKEN_NUMBER = 8;
+ public const int TOKEN_TRUE = 9;
+ public const int TOKEN_FALSE = 10;
+ public const int TOKEN_NULL = 11;
+
+ private const int BUILDER_CAPACITY = 2000;
+
+ ///
+ /// Parses the string json into a value
+ ///
+ /// A JSON string.
+ /// An ArrayList, a Hashtable, a double, a string, null, true, or false
+ public static IDictionary JsonDecode(string json) {
+ bool success = true;
+
+ return JsonDecode(json, ref success);
+ }
+
+ ///
+ /// Parses the string json into a value; and fills 'success' with the successfullness of the parse.
+ ///
+ /// A JSON string.
+ /// Successful parse?
+ /// An ArrayList, a Hashtable, a double, a string, null, true, or false
+ public static IDictionary JsonDecode(string json, ref bool success) {
+ success = true;
+ if (json != null) {
+ char[] charArray = json.ToCharArray();
+ int index = 0;
+ IDictionary value = ParseValue(charArray, ref index, ref success) as IDictionary;
+ return value;
+ }
+ return null;
+ }
+
+ protected static IDictionary ParseObject(char[] json, ref int index, ref bool success) {
+ IDictionary table = new Dictionary();
+ int token;
+
+ // {
+ NextToken(json, ref index);
+
+ bool done = false;
+ while (!done) {
+ token = LookAhead(json, index);
+ if (token == TOKEN_NONE) {
+ success = false;
+ return null;
+ } else if (token == TOKEN_COMMA) {
+ NextToken(json, ref index);
+ } else if (token == TOKEN_CURLY_CLOSE) {
+ NextToken(json, ref index);
+ return table;
+ } else {
+
+ // name
+ string name = ParseString(json, ref index, ref success);
+ if (!success) {
+ success = false;
+ return null;
+ }
+
+ // :
+ token = NextToken(json, ref index);
+ if (token != TOKEN_COLON) {
+ success = false;
+ return null;
+ }
+
+ // value
+ object value = ParseValue(json, ref index, ref success);
+ if (!success) {
+ success = false;
+ return null;
+ }
+
+ table.Add(name, value);
+ }
+ }
+
+ return table;
+ }
+
+ protected static IList